+ case TOK_MINUSMINUS:
+ eat(state, TOK_MINUSMINUS);
+ def = mk_pre_dec_expr(state, unary_expr(state));
+ break;
+ case TOK_AND:
+ eat(state, TOK_AND);
+ def = mk_addr_expr(state, cast_expr(state), 0);
+ break;
+ case TOK_STAR:
+ eat(state, TOK_STAR);
+ def = mk_deref_expr(state, read_expr(state, cast_expr(state)));
+ break;
+ case TOK_PLUS:
+ eat(state, TOK_PLUS);
+ right = read_expr(state, cast_expr(state));
+ arithmetic(state, right);
+ def = integral_promotion(state, right);
+ break;
+ case TOK_MINUS:
+ eat(state, TOK_MINUS);
+ right = read_expr(state, cast_expr(state));
+ arithmetic(state, right);
+ def = integral_promotion(state, right);
+ def = triple(state, OP_NEG, def->type, def, 0);
+ break;
+ case TOK_TILDE:
+ eat(state, TOK_TILDE);
+ right = read_expr(state, cast_expr(state));
+ integral(state, right);
+ def = integral_promotion(state, right);
+ def = triple(state, OP_INVERT, def->type, def, 0);
+ break;
+ case TOK_BANG:
+ eat(state, TOK_BANG);
+ right = read_expr(state, cast_expr(state));
+ bool(state, right);
+ def = lfalse_expr(state, right);
+ break;
+ case TOK_SIZEOF:
+ {
+ struct type *type;
+ int tok1, tok2;
+ eat(state, TOK_SIZEOF);
+ tok1 = peek(state);
+ tok2 = peek2(state);
+ if ((tok1 == TOK_LPAREN) && istype(tok2)) {
+ eat(state, TOK_LPAREN);
+ type = type_name(state);
+ eat(state, TOK_RPAREN);
+ }
+ else {
+ struct triple *expr;
+ expr = unary_expr(state);
+ type = expr->type;
+ release_expr(state, expr);
+ }
+ def = int_const(state, &ulong_type, size_of_in_bytes(state, type));
+ break;
+ }
+ case TOK_ALIGNOF:
+ {
+ struct type *type;
+ int tok1, tok2;
+ eat(state, TOK_ALIGNOF);
+ tok1 = peek(state);
+ tok2 = peek2(state);
+ if ((tok1 == TOK_LPAREN) && istype(tok2)) {
+ eat(state, TOK_LPAREN);
+ type = type_name(state);
+ eat(state, TOK_RPAREN);
+ }
+ else {
+ struct triple *expr;
+ expr = unary_expr(state);
+ type = expr->type;
+ release_expr(state, expr);
+ }
+ def = int_const(state, &ulong_type, align_of_in_bytes(state, type));
+ break;
+ }
+ case TOK_MDEFINED:
+ {
+ /* We only come here if we are called from the preprocessor */
+ struct hash_entry *ident;
+ int parens;
+ eat(state, TOK_MDEFINED);
+ parens = 0;
+ if (pp_peek(state) == TOK_LPAREN) {
+ pp_eat(state, TOK_LPAREN);
+ parens = 1;
+ }
+ ident = pp_eat(state, TOK_MIDENT)->ident;
+ if (parens) {
+ eat(state, TOK_RPAREN);
+ }
+ def = int_const(state, &int_type, ident->sym_define != 0);
+ break;
+ }
+ default:
+ def = postfix_expr(state);
+ break;
+ }
+ return def;
+}
+
+static struct triple *cast_expr(struct compile_state *state)
+{
+ struct triple *def;
+ int tok1, tok2;
+ tok1 = peek(state);
+ tok2 = peek2(state);
+ if ((tok1 == TOK_LPAREN) && istype(tok2)) {
+ struct type *type;
+ eat(state, TOK_LPAREN);
+ type = type_name(state);
+ eat(state, TOK_RPAREN);
+ def = mk_cast_expr(state, type, cast_expr(state));
+ }
+ else {
+ def = unary_expr(state);
+ }
+ return def;
+}
+
+static struct triple *mult_expr(struct compile_state *state)
+{
+ struct triple *def;
+ int done;
+ def = cast_expr(state);
+ do {
+ struct triple *left, *right;
+ struct type *result_type;
+ int tok, op, sign;
+ done = 0;
+ tok = peek(state);
+ switch(tok) {
+ case TOK_STAR:
+ case TOK_DIV:
+ case TOK_MOD:
+ left = read_expr(state, def);
+ arithmetic(state, left);
+
+ eat(state, tok);
+
+ right = read_expr(state, cast_expr(state));
+ arithmetic(state, right);
+
+ result_type = arithmetic_result(state, left, right);
+ sign = is_signed(result_type);
+ op = -1;
+ switch(tok) {
+ case TOK_STAR: op = sign? OP_SMUL : OP_UMUL; break;
+ case TOK_DIV: op = sign? OP_SDIV : OP_UDIV; break;
+ case TOK_MOD: op = sign? OP_SMOD : OP_UMOD; break;
+ }
+ def = triple(state, op, result_type, left, right);
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ } while(!done);
+ return def;
+}
+
+static struct triple *add_expr(struct compile_state *state)
+{
+ struct triple *def;
+ int done;
+ def = mult_expr(state);
+ do {
+ done = 0;
+ switch( peek(state)) {
+ case TOK_PLUS:
+ eat(state, TOK_PLUS);
+ def = mk_add_expr(state, def, mult_expr(state));
+ break;
+ case TOK_MINUS:
+ eat(state, TOK_MINUS);
+ def = mk_sub_expr(state, def, mult_expr(state));
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ } while(!done);
+ return def;
+}
+
+static struct triple *shift_expr(struct compile_state *state)
+{
+ struct triple *def;
+ int done;
+ def = add_expr(state);
+ do {
+ struct triple *left, *right;
+ int tok, op;
+ done = 0;
+ switch((tok = peek(state))) {
+ case TOK_SL:
+ case TOK_SR:
+ left = read_expr(state, def);
+ integral(state, left);
+ left = integral_promotion(state, left);
+
+ eat(state, tok);
+
+ right = read_expr(state, add_expr(state));
+ integral(state, right);
+ right = integral_promotion(state, right);
+
+ op = (tok == TOK_SL)? OP_SL :
+ is_signed(left->type)? OP_SSR: OP_USR;
+
+ def = triple(state, op, left->type, left, right);
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ } while(!done);
+ return def;
+}
+
+static struct triple *relational_expr(struct compile_state *state)
+{
+#if DEBUG_ROMCC_WARNINGS
+#warning "Extend relational exprs to work on more than arithmetic types"
+#endif
+ struct triple *def;
+ int done;
+ def = shift_expr(state);
+ do {
+ struct triple *left, *right;
+ struct type *arg_type;
+ int tok, op, sign;
+ done = 0;
+ switch((tok = peek(state))) {
+ case TOK_LESS:
+ case TOK_MORE:
+ case TOK_LESSEQ:
+ case TOK_MOREEQ:
+ left = read_expr(state, def);
+ arithmetic(state, left);
+
+ eat(state, tok);
+
+ right = read_expr(state, shift_expr(state));
+ arithmetic(state, right);
+
+ arg_type = arithmetic_result(state, left, right);
+ sign = is_signed(arg_type);
+ op = -1;
+ switch(tok) {
+ case TOK_LESS: op = sign? OP_SLESS : OP_ULESS; break;
+ case TOK_MORE: op = sign? OP_SMORE : OP_UMORE; break;
+ case TOK_LESSEQ: op = sign? OP_SLESSEQ : OP_ULESSEQ; break;
+ case TOK_MOREEQ: op = sign? OP_SMOREEQ : OP_UMOREEQ; break;
+ }
+ def = triple(state, op, &int_type, left, right);
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ } while(!done);
+ return def;
+}
+
+static struct triple *equality_expr(struct compile_state *state)
+{
+#if DEBUG_ROMCC_WARNINGS
+#warning "Extend equality exprs to work on more than arithmetic types"
+#endif
+ struct triple *def;
+ int done;
+ def = relational_expr(state);
+ do {
+ struct triple *left, *right;
+ int tok, op;
+ done = 0;
+ switch((tok = peek(state))) {
+ case TOK_EQEQ:
+ case TOK_NOTEQ:
+ left = read_expr(state, def);
+ arithmetic(state, left);
+ eat(state, tok);
+ right = read_expr(state, relational_expr(state));
+ arithmetic(state, right);
+ op = (tok == TOK_EQEQ) ? OP_EQ: OP_NOTEQ;
+ def = triple(state, op, &int_type, left, right);
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ } while(!done);
+ return def;
+}
+
+static struct triple *and_expr(struct compile_state *state)
+{
+ struct triple *def;
+ def = equality_expr(state);
+ while(peek(state) == TOK_AND) {
+ struct triple *left, *right;
+ struct type *result_type;
+ left = read_expr(state, def);
+ integral(state, left);
+ eat(state, TOK_AND);
+ right = read_expr(state, equality_expr(state));
+ integral(state, right);
+ result_type = arithmetic_result(state, left, right);
+ def = triple(state, OP_AND, result_type, left, right);
+ }
+ return def;
+}
+
+static struct triple *xor_expr(struct compile_state *state)
+{
+ struct triple *def;
+ def = and_expr(state);
+ while(peek(state) == TOK_XOR) {
+ struct triple *left, *right;
+ struct type *result_type;
+ left = read_expr(state, def);
+ integral(state, left);
+ eat(state, TOK_XOR);
+ right = read_expr(state, and_expr(state));
+ integral(state, right);
+ result_type = arithmetic_result(state, left, right);
+ def = triple(state, OP_XOR, result_type, left, right);
+ }
+ return def;
+}
+
+static struct triple *or_expr(struct compile_state *state)
+{
+ struct triple *def;
+ def = xor_expr(state);
+ while(peek(state) == TOK_OR) {
+ struct triple *left, *right;
+ struct type *result_type;
+ left = read_expr(state, def);
+ integral(state, left);
+ eat(state, TOK_OR);
+ right = read_expr(state, xor_expr(state));
+ integral(state, right);
+ result_type = arithmetic_result(state, left, right);
+ def = triple(state, OP_OR, result_type, left, right);
+ }
+ return def;
+}
+
+static struct triple *land_expr(struct compile_state *state)
+{
+ struct triple *def;
+ def = or_expr(state);
+ while(peek(state) == TOK_LOGAND) {
+ struct triple *left, *right;
+ left = read_expr(state, def);
+ bool(state, left);
+ eat(state, TOK_LOGAND);
+ right = read_expr(state, or_expr(state));
+ bool(state, right);
+
+ def = mkland_expr(state,
+ ltrue_expr(state, left),
+ ltrue_expr(state, right));
+ }
+ return def;
+}
+
+static struct triple *lor_expr(struct compile_state *state)
+{
+ struct triple *def;
+ def = land_expr(state);
+ while(peek(state) == TOK_LOGOR) {
+ struct triple *left, *right;
+ left = read_expr(state, def);
+ bool(state, left);
+ eat(state, TOK_LOGOR);
+ right = read_expr(state, land_expr(state));
+ bool(state, right);
+
+ def = mklor_expr(state,
+ ltrue_expr(state, left),
+ ltrue_expr(state, right));
+ }
+ return def;
+}
+
+static struct triple *conditional_expr(struct compile_state *state)
+{
+ struct triple *def;
+ def = lor_expr(state);
+ if (peek(state) == TOK_QUEST) {
+ struct triple *test, *left, *right;
+ bool(state, def);
+ test = ltrue_expr(state, read_expr(state, def));
+ eat(state, TOK_QUEST);
+ left = read_expr(state, expr(state));
+ eat(state, TOK_COLON);
+ right = read_expr(state, conditional_expr(state));
+
+ def = mkcond_expr(state, test, left, right);
+ }
+ return def;
+}
+
+struct cv_triple {
+ struct triple *val;
+ int id;
+};
+
+static void set_cv(struct compile_state *state, struct cv_triple *cv,
+ struct triple *dest, struct triple *val)
+{
+ if (cv[dest->id].val) {
+ free_triple(state, cv[dest->id].val);
+ }
+ cv[dest->id].val = val;
+}
+static struct triple *get_cv(struct compile_state *state, struct cv_triple *cv,
+ struct triple *src)
+{
+ return cv[src->id].val;
+}
+
+static struct triple *eval_const_expr(
+ struct compile_state *state, struct triple *expr)
+{
+ struct triple *def;
+ if (is_const(expr)) {
+ def = expr;
+ }
+ else {
+ /* If we don't start out as a constant simplify into one */
+ struct triple *head, *ptr;
+ struct cv_triple *cv;
+ int i, count;
+ head = label(state); /* dummy initial triple */
+ flatten(state, head, expr);
+ count = 1;
+ for(ptr = head->next; ptr != head; ptr = ptr->next) {
+ count++;
+ }
+ cv = xcmalloc(sizeof(struct cv_triple)*count, "const value vector");
+ i = 1;
+ for(ptr = head->next; ptr != head; ptr = ptr->next) {
+ cv[i].val = 0;
+ cv[i].id = ptr->id;
+ ptr->id = i;
+ i++;
+ }
+ ptr = head->next;
+ do {
+ valid_ins(state, ptr);
+ if ((ptr->op == OP_PHI) || (ptr->op == OP_LIST)) {
+ internal_error(state, ptr,
+ "unexpected %s in constant expression",
+ tops(ptr->op));
+ }
+ else if (ptr->op == OP_LIST) {
+ }
+ else if (triple_is_structural(state, ptr)) {
+ ptr = ptr->next;
+ }
+ else if (triple_is_ubranch(state, ptr)) {
+ ptr = TARG(ptr, 0);
+ }
+ else if (triple_is_cbranch(state, ptr)) {
+ struct triple *cond_val;
+ cond_val = get_cv(state, cv, RHS(ptr, 0));
+ if (!cond_val || !is_const(cond_val) ||
+ (cond_val->op != OP_INTCONST))
+ {
+ internal_error(state, ptr, "bad branch condition");
+ }
+ if (cond_val->u.cval == 0) {
+ ptr = ptr->next;
+ } else {
+ ptr = TARG(ptr, 0);
+ }
+ }
+ else if (triple_is_branch(state, ptr)) {
+ error(state, ptr, "bad branch type in constant expression");
+ }
+ else if (ptr->op == OP_WRITE) {
+ struct triple *val;
+ val = get_cv(state, cv, RHS(ptr, 0));
+
+ set_cv(state, cv, MISC(ptr, 0),
+ copy_triple(state, val));
+ set_cv(state, cv, ptr,
+ copy_triple(state, val));
+ ptr = ptr->next;
+ }
+ else if (ptr->op == OP_READ) {
+ set_cv(state, cv, ptr,
+ copy_triple(state,
+ get_cv(state, cv, RHS(ptr, 0))));
+ ptr = ptr->next;
+ }
+ else if (triple_is_pure(state, ptr, cv[ptr->id].id)) {
+ struct triple *val, **rhs;
+ val = copy_triple(state, ptr);
+ rhs = triple_rhs(state, val, 0);
+ for(; rhs; rhs = triple_rhs(state, val, rhs)) {
+ if (!*rhs) {
+ internal_error(state, ptr, "Missing rhs");
+ }
+ *rhs = get_cv(state, cv, *rhs);
+ }
+ simplify(state, val);
+ set_cv(state, cv, ptr, val);
+ ptr = ptr->next;
+ }
+ else {
+ error(state, ptr, "impure operation in constant expression");
+ }
+
+ } while(ptr != head);
+
+ /* Get the result value */
+ def = get_cv(state, cv, head->prev);
+ cv[head->prev->id].val = 0;
+
+ /* Free the temporary values */
+ for(i = 0; i < count; i++) {
+ if (cv[i].val) {
+ free_triple(state, cv[i].val);
+ cv[i].val = 0;
+ }
+ }
+ xfree(cv);
+ /* Free the intermediate expressions */
+ while(head->next != head) {
+ release_triple(state, head->next);
+ }
+ free_triple(state, head);
+ }
+ if (!is_const(def)) {
+ error(state, expr, "Not a constant expression");
+ }
+ return def;
+}
+
+static struct triple *constant_expr(struct compile_state *state)
+{
+ return eval_const_expr(state, conditional_expr(state));
+}
+
+static struct triple *assignment_expr(struct compile_state *state)
+{
+ struct triple *def, *left, *right;
+ int tok, op, sign;
+ /* The C grammer in K&R shows assignment expressions
+ * only taking unary expressions as input on their
+ * left hand side. But specifies the precedence of
+ * assignemnt as the lowest operator except for comma.
+ *
+ * Allowing conditional expressions on the left hand side
+ * of an assignement results in a grammar that accepts
+ * a larger set of statements than standard C. As long
+ * as the subset of the grammar that is standard C behaves
+ * correctly this should cause no problems.
+ *
+ * For the extra token strings accepted by the grammar
+ * none of them should produce a valid lvalue, so they
+ * should not produce functioning programs.
+ *
+ * GCC has this bug as well, so surprises should be minimal.
+ */
+ def = conditional_expr(state);
+ left = def;
+ switch((tok = peek(state))) {
+ case TOK_EQ:
+ lvalue(state, left);
+ eat(state, TOK_EQ);
+ def = write_expr(state, left,
+ read_expr(state, assignment_expr(state)));
+ break;
+ case TOK_TIMESEQ:
+ case TOK_DIVEQ:
+ case TOK_MODEQ:
+ lvalue(state, left);
+ arithmetic(state, left);
+ eat(state, tok);
+ right = read_expr(state, assignment_expr(state));
+ arithmetic(state, right);
+
+ sign = is_signed(left->type);
+ op = -1;
+ switch(tok) {
+ case TOK_TIMESEQ: op = sign? OP_SMUL : OP_UMUL; break;
+ case TOK_DIVEQ: op = sign? OP_SDIV : OP_UDIV; break;
+ case TOK_MODEQ: op = sign? OP_SMOD : OP_UMOD; break;
+ }
+ def = write_expr(state, left,
+ triple(state, op, left->type,
+ read_expr(state, left), right));
+ break;
+ case TOK_PLUSEQ:
+ lvalue(state, left);
+ eat(state, TOK_PLUSEQ);
+ def = write_expr(state, left,
+ mk_add_expr(state, left, assignment_expr(state)));
+ break;
+ case TOK_MINUSEQ:
+ lvalue(state, left);
+ eat(state, TOK_MINUSEQ);
+ def = write_expr(state, left,
+ mk_sub_expr(state, left, assignment_expr(state)));
+ break;
+ case TOK_SLEQ:
+ case TOK_SREQ:
+ case TOK_ANDEQ:
+ case TOK_XOREQ:
+ case TOK_OREQ:
+ lvalue(state, left);
+ integral(state, left);
+ eat(state, tok);
+ right = read_expr(state, assignment_expr(state));
+ integral(state, right);
+ right = integral_promotion(state, right);
+ sign = is_signed(left->type);
+ op = -1;
+ switch(tok) {
+ case TOK_SLEQ: op = OP_SL; break;
+ case TOK_SREQ: op = sign? OP_SSR: OP_USR; break;
+ case TOK_ANDEQ: op = OP_AND; break;
+ case TOK_XOREQ: op = OP_XOR; break;
+ case TOK_OREQ: op = OP_OR; break;
+ }
+ def = write_expr(state, left,
+ triple(state, op, left->type,
+ read_expr(state, left), right));
+ break;
+ }
+ return def;
+}
+
+static struct triple *expr(struct compile_state *state)
+{
+ struct triple *def;
+ def = assignment_expr(state);
+ while(peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ def = mkprog(state, def, assignment_expr(state), 0UL);
+ }
+ return def;
+}
+
+static void expr_statement(struct compile_state *state, struct triple *first)
+{
+ if (peek(state) != TOK_SEMI) {
+ /* lvalue conversions always apply except when certian operators
+ * are applied. I apply the lvalue conversions here
+ * as I know no more operators will be applied.
+ */
+ flatten(state, first, lvalue_conversion(state, expr(state)));
+ }
+ eat(state, TOK_SEMI);
+}
+
+static void if_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *test, *jmp1, *jmp2, *middle, *end;
+
+ jmp1 = jmp2 = middle = 0;
+ eat(state, TOK_IF);
+ eat(state, TOK_LPAREN);
+ test = expr(state);
+ bool(state, test);
+ /* Cleanup and invert the test */
+ test = lfalse_expr(state, read_expr(state, test));
+ eat(state, TOK_RPAREN);
+ /* Generate the needed pieces */
+ middle = label(state);
+ jmp1 = branch(state, middle, test);
+ /* Thread the pieces together */
+ flatten(state, first, test);
+ flatten(state, first, jmp1);
+ flatten(state, first, label(state));
+ statement(state, first);
+ if (peek(state) == TOK_ELSE) {
+ eat(state, TOK_ELSE);
+ /* Generate the rest of the pieces */
+ end = label(state);
+ jmp2 = branch(state, end, 0);
+ /* Thread them together */
+ flatten(state, first, jmp2);
+ flatten(state, first, middle);
+ statement(state, first);
+ flatten(state, first, end);
+ }
+ else {
+ flatten(state, first, middle);
+ }
+}
+
+static void for_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *head, *test, *tail, *jmp1, *jmp2, *end;
+ struct triple *label1, *label2, *label3;
+ struct hash_entry *ident;
+
+ eat(state, TOK_FOR);
+ eat(state, TOK_LPAREN);
+ head = test = tail = jmp1 = jmp2 = 0;
+ if (peek(state) != TOK_SEMI) {
+ head = expr(state);
+ }
+ eat(state, TOK_SEMI);
+ if (peek(state) != TOK_SEMI) {
+ test = expr(state);
+ bool(state, test);
+ test = ltrue_expr(state, read_expr(state, test));
+ }
+ eat(state, TOK_SEMI);
+ if (peek(state) != TOK_RPAREN) {
+ tail = expr(state);
+ }
+ eat(state, TOK_RPAREN);
+ /* Generate the needed pieces */
+ label1 = label(state);
+ label2 = label(state);
+ label3 = label(state);
+ if (test) {
+ jmp1 = branch(state, label3, 0);
+ jmp2 = branch(state, label1, test);
+ }
+ else {
+ jmp2 = branch(state, label1, 0);
+ }
+ end = label(state);
+ /* Remember where break and continue go */
+ start_scope(state);
+ ident = state->i_break;
+ symbol(state, ident, &ident->sym_ident, end, end->type);
+ ident = state->i_continue;
+ symbol(state, ident, &ident->sym_ident, label2, label2->type);
+ /* Now include the body */
+ flatten(state, first, head);
+ flatten(state, first, jmp1);
+ flatten(state, first, label1);
+ statement(state, first);
+ flatten(state, first, label2);
+ flatten(state, first, tail);
+ flatten(state, first, label3);
+ flatten(state, first, test);
+ flatten(state, first, jmp2);
+ flatten(state, first, end);
+ /* Cleanup the break/continue scope */
+ end_scope(state);
+}
+
+static void while_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *label1, *test, *label2, *jmp1, *jmp2, *end;
+ struct hash_entry *ident;
+ eat(state, TOK_WHILE);
+ eat(state, TOK_LPAREN);
+ test = expr(state);
+ bool(state, test);
+ test = ltrue_expr(state, read_expr(state, test));
+ eat(state, TOK_RPAREN);
+ /* Generate the needed pieces */
+ label1 = label(state);
+ label2 = label(state);
+ jmp1 = branch(state, label2, 0);
+ jmp2 = branch(state, label1, test);
+ end = label(state);
+ /* Remember where break and continue go */
+ start_scope(state);
+ ident = state->i_break;
+ symbol(state, ident, &ident->sym_ident, end, end->type);
+ ident = state->i_continue;
+ symbol(state, ident, &ident->sym_ident, label2, label2->type);
+ /* Thread them together */
+ flatten(state, first, jmp1);
+ flatten(state, first, label1);
+ statement(state, first);
+ flatten(state, first, label2);
+ flatten(state, first, test);
+ flatten(state, first, jmp2);
+ flatten(state, first, end);
+ /* Cleanup the break/continue scope */
+ end_scope(state);
+}
+
+static void do_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *label1, *label2, *test, *end;
+ struct hash_entry *ident;
+ eat(state, TOK_DO);
+ /* Generate the needed pieces */
+ label1 = label(state);
+ label2 = label(state);
+ end = label(state);
+ /* Remember where break and continue go */
+ start_scope(state);
+ ident = state->i_break;
+ symbol(state, ident, &ident->sym_ident, end, end->type);
+ ident = state->i_continue;
+ symbol(state, ident, &ident->sym_ident, label2, label2->type);
+ /* Now include the body */
+ flatten(state, first, label1);
+ statement(state, first);
+ /* Cleanup the break/continue scope */
+ end_scope(state);
+ /* Eat the rest of the loop */
+ eat(state, TOK_WHILE);
+ eat(state, TOK_LPAREN);
+ test = read_expr(state, expr(state));
+ bool(state, test);
+ eat(state, TOK_RPAREN);
+ eat(state, TOK_SEMI);
+ /* Thread the pieces together */
+ test = ltrue_expr(state, test);
+ flatten(state, first, label2);
+ flatten(state, first, test);
+ flatten(state, first, branch(state, label1, test));
+ flatten(state, first, end);
+}
+
+
+static void return_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *jmp, *mv, *dest, *var, *val;
+ int last;
+ eat(state, TOK_RETURN);
+
+#if DEBUG_ROMCC_WARNINGS
+#warning "FIXME implement a more general excess branch elimination"
+#endif
+ val = 0;
+ /* If we have a return value do some more work */
+ if (peek(state) != TOK_SEMI) {
+ val = read_expr(state, expr(state));
+ }
+ eat(state, TOK_SEMI);
+
+ /* See if this last statement in a function */
+ last = ((peek(state) == TOK_RBRACE) &&
+ (state->scope_depth == GLOBAL_SCOPE_DEPTH +2));
+
+ /* Find the return variable */
+ var = fresult(state, state->main_function);
+
+ /* Find the return destination */
+ dest = state->i_return->sym_ident->def;
+ mv = jmp = 0;
+ /* If needed generate a jump instruction */
+ if (!last) {
+ jmp = branch(state, dest, 0);
+ }
+ /* If needed generate an assignment instruction */
+ if (val) {
+ mv = write_expr(state, deref_index(state, var, 1), val);
+ }
+ /* Now put the code together */
+ if (mv) {
+ flatten(state, first, mv);
+ flatten(state, first, jmp);
+ }
+ else if (jmp) {
+ flatten(state, first, jmp);
+ }
+}
+
+static void break_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *dest;
+ eat(state, TOK_BREAK);
+ eat(state, TOK_SEMI);
+ if (!state->i_break->sym_ident) {
+ error(state, 0, "break statement not within loop or switch");
+ }
+ dest = state->i_break->sym_ident->def;
+ flatten(state, first, branch(state, dest, 0));
+}
+
+static void continue_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *dest;
+ eat(state, TOK_CONTINUE);
+ eat(state, TOK_SEMI);
+ if (!state->i_continue->sym_ident) {
+ error(state, 0, "continue statement outside of a loop");
+ }
+ dest = state->i_continue->sym_ident->def;
+ flatten(state, first, branch(state, dest, 0));
+}
+
+static void goto_statement(struct compile_state *state, struct triple *first)
+{
+ struct hash_entry *ident;
+ eat(state, TOK_GOTO);
+ ident = eat(state, TOK_IDENT)->ident;
+ if (!ident->sym_label) {
+ /* If this is a forward branch allocate the label now,
+ * it will be flattend in the appropriate location later.
+ */
+ struct triple *ins;
+ ins = label(state);
+ label_symbol(state, ident, ins, FUNCTION_SCOPE_DEPTH);
+ }
+ eat(state, TOK_SEMI);
+
+ flatten(state, first, branch(state, ident->sym_label->def, 0));
+}
+
+static void labeled_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *ins;
+ struct hash_entry *ident;
+
+ ident = eat(state, TOK_IDENT)->ident;
+ if (ident->sym_label && ident->sym_label->def) {
+ ins = ident->sym_label->def;
+ put_occurance(ins->occurance);
+ ins->occurance = new_occurance(state);
+ }
+ else {
+ ins = label(state);
+ label_symbol(state, ident, ins, FUNCTION_SCOPE_DEPTH);
+ }
+ if (ins->id & TRIPLE_FLAG_FLATTENED) {
+ error(state, 0, "label %s already defined", ident->name);
+ }
+ flatten(state, first, ins);
+
+ eat(state, TOK_COLON);
+ statement(state, first);
+}
+
+static void switch_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *value, *top, *end, *dbranch;
+ struct hash_entry *ident;
+
+ /* See if we have a valid switch statement */
+ eat(state, TOK_SWITCH);
+ eat(state, TOK_LPAREN);
+ value = expr(state);
+ integral(state, value);
+ value = read_expr(state, value);
+ eat(state, TOK_RPAREN);
+ /* Generate the needed pieces */
+ top = label(state);
+ end = label(state);
+ dbranch = branch(state, end, 0);
+ /* Remember where case branches and break goes */
+ start_scope(state);
+ ident = state->i_switch;
+ symbol(state, ident, &ident->sym_ident, value, value->type);
+ ident = state->i_case;
+ symbol(state, ident, &ident->sym_ident, top, top->type);
+ ident = state->i_break;
+ symbol(state, ident, &ident->sym_ident, end, end->type);
+ ident = state->i_default;
+ symbol(state, ident, &ident->sym_ident, dbranch, dbranch->type);
+ /* Thread them together */
+ flatten(state, first, value);
+ flatten(state, first, top);
+ flatten(state, first, dbranch);
+ statement(state, first);
+ flatten(state, first, end);
+ /* Cleanup the switch scope */
+ end_scope(state);
+}
+
+static void case_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *cvalue, *dest, *test, *jmp;
+ struct triple *ptr, *value, *top, *dbranch;
+
+ /* See if w have a valid case statement */
+ eat(state, TOK_CASE);
+ cvalue = constant_expr(state);
+ integral(state, cvalue);
+ if (cvalue->op != OP_INTCONST) {
+ error(state, 0, "integer constant expected");
+ }
+ eat(state, TOK_COLON);
+ if (!state->i_case->sym_ident) {
+ error(state, 0, "case statement not within a switch");
+ }
+
+ /* Lookup the interesting pieces */
+ top = state->i_case->sym_ident->def;
+ value = state->i_switch->sym_ident->def;
+ dbranch = state->i_default->sym_ident->def;
+
+ /* See if this case label has already been used */
+ for(ptr = top; ptr != dbranch; ptr = ptr->next) {
+ if (ptr->op != OP_EQ) {
+ continue;
+ }
+ if (RHS(ptr, 1)->u.cval == cvalue->u.cval) {
+ error(state, 0, "duplicate case %d statement",
+ cvalue->u.cval);
+ }
+ }
+ /* Generate the needed pieces */
+ dest = label(state);
+ test = triple(state, OP_EQ, &int_type, value, cvalue);
+ jmp = branch(state, dest, test);
+ /* Thread the pieces together */
+ flatten(state, dbranch, test);
+ flatten(state, dbranch, jmp);
+ flatten(state, dbranch, label(state));
+ flatten(state, first, dest);
+ statement(state, first);
+}
+
+static void default_statement(struct compile_state *state, struct triple *first)
+{
+ struct triple *dest;
+ struct triple *dbranch, *end;
+
+ /* See if we have a valid default statement */
+ eat(state, TOK_DEFAULT);
+ eat(state, TOK_COLON);
+
+ if (!state->i_case->sym_ident) {
+ error(state, 0, "default statement not within a switch");
+ }
+
+ /* Lookup the interesting pieces */
+ dbranch = state->i_default->sym_ident->def;
+ end = state->i_break->sym_ident->def;
+
+ /* See if a default statement has already happened */
+ if (TARG(dbranch, 0) != end) {
+ error(state, 0, "duplicate default statement");
+ }
+
+ /* Generate the needed pieces */
+ dest = label(state);
+
+ /* Blame the branch on the default statement */
+ put_occurance(dbranch->occurance);
+ dbranch->occurance = new_occurance(state);
+
+ /* Thread the pieces together */
+ TARG(dbranch, 0) = dest;
+ use_triple(dest, dbranch);
+ flatten(state, first, dest);
+ statement(state, first);
+}
+
+static void asm_statement(struct compile_state *state, struct triple *first)
+{
+ struct asm_info *info;
+ struct {
+ struct triple *constraint;
+ struct triple *expr;
+ } out_param[MAX_LHS], in_param[MAX_RHS], clob_param[MAX_LHS];
+ struct triple *def, *asm_str;
+ int out, in, clobbers, more, colons, i;
+ int flags;
+
+ flags = 0;
+ eat(state, TOK_ASM);
+ /* For now ignore the qualifiers */
+ switch(peek(state)) {
+ case TOK_CONST:
+ eat(state, TOK_CONST);
+ break;
+ case TOK_VOLATILE:
+ eat(state, TOK_VOLATILE);
+ flags |= TRIPLE_FLAG_VOLATILE;
+ break;
+ }
+ eat(state, TOK_LPAREN);
+ asm_str = string_constant(state);
+
+ colons = 0;
+ out = in = clobbers = 0;
+ /* Outputs */
+ if ((colons == 0) && (peek(state) == TOK_COLON)) {
+ eat(state, TOK_COLON);
+ colons++;
+ more = (peek(state) == TOK_LIT_STRING);
+ while(more) {
+ struct triple *var;
+ struct triple *constraint;
+ char *str;
+ more = 0;
+ if (out > MAX_LHS) {
+ error(state, 0, "Maximum output count exceeded.");
+ }
+ constraint = string_constant(state);
+ str = constraint->u.blob;
+ if (str[0] != '=') {
+ error(state, 0, "Output constraint does not start with =");
+ }
+ constraint->u.blob = str + 1;
+ eat(state, TOK_LPAREN);
+ var = conditional_expr(state);
+ eat(state, TOK_RPAREN);
+
+ lvalue(state, var);
+ out_param[out].constraint = constraint;
+ out_param[out].expr = var;
+ if (peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ more = 1;
+ }
+ out++;
+ }
+ }
+ /* Inputs */
+ if ((colons == 1) && (peek(state) == TOK_COLON)) {
+ eat(state, TOK_COLON);
+ colons++;
+ more = (peek(state) == TOK_LIT_STRING);
+ while(more) {
+ struct triple *val;
+ struct triple *constraint;
+ char *str;
+ more = 0;
+ if (in > MAX_RHS) {
+ error(state, 0, "Maximum input count exceeded.");
+ }
+ constraint = string_constant(state);
+ str = constraint->u.blob;
+ if (digitp(str[0] && str[1] == '\0')) {
+ int val;
+ val = digval(str[0]);
+ if ((val < 0) || (val >= out)) {
+ error(state, 0, "Invalid input constraint %d", val);
+ }
+ }
+ eat(state, TOK_LPAREN);
+ val = conditional_expr(state);
+ eat(state, TOK_RPAREN);
+
+ in_param[in].constraint = constraint;
+ in_param[in].expr = val;
+ if (peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ more = 1;
+ }
+ in++;
+ }
+ }
+
+ /* Clobber */
+ if ((colons == 2) && (peek(state) == TOK_COLON)) {
+ eat(state, TOK_COLON);
+ colons++;
+ more = (peek(state) == TOK_LIT_STRING);
+ while(more) {
+ struct triple *clobber;
+ more = 0;
+ if ((clobbers + out) > MAX_LHS) {
+ error(state, 0, "Maximum clobber limit exceeded.");
+ }
+ clobber = string_constant(state);
+
+ clob_param[clobbers].constraint = clobber;
+ if (peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ more = 1;
+ }
+ clobbers++;
+ }
+ }
+ eat(state, TOK_RPAREN);
+ eat(state, TOK_SEMI);
+
+
+ info = xcmalloc(sizeof(*info), "asm_info");
+ info->str = asm_str->u.blob;
+ free_triple(state, asm_str);
+
+ def = new_triple(state, OP_ASM, &void_type, clobbers + out, in);
+ def->u.ainfo = info;
+ def->id |= flags;
+
+ /* Find the register constraints */
+ for(i = 0; i < out; i++) {
+ struct triple *constraint;
+ constraint = out_param[i].constraint;
+ info->tmpl.lhs[i] = arch_reg_constraint(state,
+ out_param[i].expr->type, constraint->u.blob);
+ free_triple(state, constraint);
+ }
+ for(; i - out < clobbers; i++) {
+ struct triple *constraint;
+ constraint = clob_param[i - out].constraint;
+ info->tmpl.lhs[i] = arch_reg_clobber(state, constraint->u.blob);
+ free_triple(state, constraint);
+ }
+ for(i = 0; i < in; i++) {
+ struct triple *constraint;
+ const char *str;
+ constraint = in_param[i].constraint;
+ str = constraint->u.blob;
+ if (digitp(str[0]) && str[1] == '\0') {
+ struct reg_info cinfo;
+ int val;
+ val = digval(str[0]);
+ cinfo.reg = info->tmpl.lhs[val].reg;
+ cinfo.regcm = arch_type_to_regcm(state, in_param[i].expr->type);
+ cinfo.regcm &= info->tmpl.lhs[val].regcm;
+ if (cinfo.reg == REG_UNSET) {
+ cinfo.reg = REG_VIRT0 + val;
+ }
+ if (cinfo.regcm == 0) {
+ error(state, 0, "No registers for %d", val);
+ }
+ info->tmpl.lhs[val] = cinfo;
+ info->tmpl.rhs[i] = cinfo;
+
+ } else {
+ info->tmpl.rhs[i] = arch_reg_constraint(state,
+ in_param[i].expr->type, str);
+ }
+ free_triple(state, constraint);
+ }
+
+ /* Now build the helper expressions */
+ for(i = 0; i < in; i++) {
+ RHS(def, i) = read_expr(state, in_param[i].expr);
+ }
+ flatten(state, first, def);
+ for(i = 0; i < (out + clobbers); i++) {
+ struct type *type;
+ struct triple *piece;
+ if (i < out) {
+ type = out_param[i].expr->type;
+ } else {
+ size_t size = arch_reg_size(info->tmpl.lhs[i].reg);
+ if (size >= SIZEOF_LONG) {
+ type = &ulong_type;
+ }
+ else if (size >= SIZEOF_INT) {
+ type = &uint_type;
+ }
+ else if (size >= SIZEOF_SHORT) {
+ type = &ushort_type;
+ }
+ else {
+ type = &uchar_type;
+ }
+ }
+ piece = triple(state, OP_PIECE, type, def, 0);
+ piece->u.cval = i;
+ LHS(def, i) = piece;
+ flatten(state, first, piece);
+ }
+ /* And write the helpers to their destinations */
+ for(i = 0; i < out; i++) {
+ struct triple *piece;
+ piece = LHS(def, i);
+ flatten(state, first,
+ write_expr(state, out_param[i].expr, piece));
+ }
+}
+
+
+static int isdecl(int tok)
+{
+ switch(tok) {
+ case TOK_AUTO:
+ case TOK_REGISTER:
+ case TOK_STATIC:
+ case TOK_EXTERN:
+ case TOK_TYPEDEF:
+ case TOK_CONST:
+ case TOK_RESTRICT:
+ case TOK_VOLATILE:
+ case TOK_VOID:
+ case TOK_CHAR:
+ case TOK_SHORT:
+ case TOK_INT:
+ case TOK_LONG:
+ case TOK_FLOAT:
+ case TOK_DOUBLE:
+ case TOK_SIGNED:
+ case TOK_UNSIGNED:
+ case TOK_STRUCT:
+ case TOK_UNION:
+ case TOK_ENUM:
+ case TOK_TYPE_NAME: /* typedef name */
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void compound_statement(struct compile_state *state, struct triple *first)
+{
+ eat(state, TOK_LBRACE);
+ start_scope(state);
+
+ /* statement-list opt */
+ while (peek(state) != TOK_RBRACE) {
+ statement(state, first);
+ }
+ end_scope(state);
+ eat(state, TOK_RBRACE);
+}
+
+static void statement(struct compile_state *state, struct triple *first)
+{
+ int tok;
+ tok = peek(state);
+ if (tok == TOK_LBRACE) {
+ compound_statement(state, first);
+ }
+ else if (tok == TOK_IF) {
+ if_statement(state, first);
+ }
+ else if (tok == TOK_FOR) {
+ for_statement(state, first);
+ }
+ else if (tok == TOK_WHILE) {
+ while_statement(state, first);
+ }
+ else if (tok == TOK_DO) {
+ do_statement(state, first);
+ }
+ else if (tok == TOK_RETURN) {
+ return_statement(state, first);
+ }
+ else if (tok == TOK_BREAK) {
+ break_statement(state, first);
+ }
+ else if (tok == TOK_CONTINUE) {
+ continue_statement(state, first);
+ }
+ else if (tok == TOK_GOTO) {
+ goto_statement(state, first);
+ }
+ else if (tok == TOK_SWITCH) {
+ switch_statement(state, first);
+ }
+ else if (tok == TOK_ASM) {
+ asm_statement(state, first);
+ }
+ else if ((tok == TOK_IDENT) && (peek2(state) == TOK_COLON)) {
+ labeled_statement(state, first);
+ }
+ else if (tok == TOK_CASE) {
+ case_statement(state, first);
+ }
+ else if (tok == TOK_DEFAULT) {
+ default_statement(state, first);
+ }
+ else if (isdecl(tok)) {
+ /* This handles C99 intermixing of statements and decls */
+ decl(state, first);
+ }
+ else {
+ expr_statement(state, first);
+ }
+}
+
+static struct type *param_decl(struct compile_state *state)
+{
+ struct type *type;
+ struct hash_entry *ident;
+ /* Cheat so the declarator will know we are not global */
+ start_scope(state);
+ ident = 0;
+ type = decl_specifiers(state);
+ type = declarator(state, type, &ident, 0);
+ type->field_ident = ident;
+ end_scope(state);
+ return type;
+}
+
+static struct type *param_type_list(struct compile_state *state, struct type *type)
+{
+ struct type *ftype, **next;
+ ftype = new_type(TYPE_FUNCTION | (type->type & STOR_MASK), type, param_decl(state));
+ next = &ftype->right;
+ ftype->elements = 1;
+ while(peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ if (peek(state) == TOK_DOTS) {
+ eat(state, TOK_DOTS);
+ error(state, 0, "variadic functions not supported");
+ }
+ else {
+ *next = new_type(TYPE_PRODUCT, *next, param_decl(state));
+ next = &((*next)->right);
+ ftype->elements++;
+ }
+ }
+ return ftype;
+}
+
+static struct type *type_name(struct compile_state *state)
+{
+ struct type *type;
+ type = specifier_qualifier_list(state);
+ /* abstract-declarator (may consume no tokens) */
+ type = declarator(state, type, 0, 0);
+ return type;
+}
+
+static struct type *direct_declarator(
+ struct compile_state *state, struct type *type,
+ struct hash_entry **pident, int need_ident)
+{
+ struct hash_entry *ident;
+ struct type *outer;
+ int op;
+ outer = 0;
+ arrays_complete(state, type);
+ switch(peek(state)) {
+ case TOK_IDENT:
+ ident = eat(state, TOK_IDENT)->ident;
+ if (!ident) {
+ error(state, 0, "Unexpected identifier found");
+ }
+ /* The name of what we are declaring */
+ *pident = ident;
+ break;
+ case TOK_LPAREN:
+ eat(state, TOK_LPAREN);
+ outer = declarator(state, type, pident, need_ident);
+ eat(state, TOK_RPAREN);
+ break;
+ default:
+ if (need_ident) {
+ error(state, 0, "Identifier expected");
+ }
+ break;
+ }
+ do {
+ op = 1;
+ arrays_complete(state, type);
+ switch(peek(state)) {
+ case TOK_LPAREN:
+ eat(state, TOK_LPAREN);
+ type = param_type_list(state, type);
+ eat(state, TOK_RPAREN);
+ break;
+ case TOK_LBRACKET:
+ {
+ unsigned int qualifiers;
+ struct triple *value;
+ value = 0;
+ eat(state, TOK_LBRACKET);
+ if (peek(state) != TOK_RBRACKET) {
+ value = constant_expr(state);
+ integral(state, value);
+ }
+ eat(state, TOK_RBRACKET);
+
+ qualifiers = type->type & (QUAL_MASK | STOR_MASK);
+ type = new_type(TYPE_ARRAY | qualifiers, type, 0);
+ if (value) {
+ type->elements = value->u.cval;
+ free_triple(state, value);
+ } else {
+ type->elements = ELEMENT_COUNT_UNSPECIFIED;
+ op = 0;
+ }
+ }
+ break;
+ default:
+ op = 0;
+ break;
+ }
+ } while(op);
+ if (outer) {
+ struct type *inner;
+ arrays_complete(state, type);
+ FINISHME();
+ for(inner = outer; inner->left; inner = inner->left)
+ ;
+ inner->left = type;
+ type = outer;
+ }
+ return type;
+}
+
+static struct type *declarator(
+ struct compile_state *state, struct type *type,
+ struct hash_entry **pident, int need_ident)
+{
+ while(peek(state) == TOK_STAR) {
+ eat(state, TOK_STAR);
+ type = new_type(TYPE_POINTER | (type->type & STOR_MASK), type, 0);
+ }
+ type = direct_declarator(state, type, pident, need_ident);
+ return type;
+}
+
+static struct type *typedef_name(
+ struct compile_state *state, unsigned int specifiers)
+{
+ struct hash_entry *ident;
+ struct type *type;
+ ident = eat(state, TOK_TYPE_NAME)->ident;
+ type = ident->sym_ident->type;
+ specifiers |= type->type & QUAL_MASK;
+ if ((specifiers & (STOR_MASK | QUAL_MASK)) !=
+ (type->type & (STOR_MASK | QUAL_MASK))) {
+ type = clone_type(specifiers, type);
+ }
+ return type;
+}
+
+static struct type *enum_specifier(
+ struct compile_state *state, unsigned int spec)
+{
+ struct hash_entry *ident;
+ ulong_t base;
+ int tok;
+ struct type *enum_type;
+ enum_type = 0;
+ ident = 0;
+ eat(state, TOK_ENUM);
+ tok = peek(state);
+ if ((tok == TOK_IDENT) || (tok == TOK_ENUM_CONST) || (tok == TOK_TYPE_NAME)) {
+ ident = eat(state, tok)->ident;
+ }
+ base = 0;
+ if (!ident || (peek(state) == TOK_LBRACE)) {
+ struct type **next;
+ eat(state, TOK_LBRACE);
+ enum_type = new_type(TYPE_ENUM | spec, 0, 0);
+ enum_type->type_ident = ident;
+ next = &enum_type->right;
+ do {
+ struct hash_entry *eident;
+ struct triple *value;
+ struct type *entry;
+ eident = eat(state, TOK_IDENT)->ident;
+ if (eident->sym_ident) {
+ error(state, 0, "%s already declared",
+ eident->name);
+ }
+ eident->tok = TOK_ENUM_CONST;
+ if (peek(state) == TOK_EQ) {
+ struct triple *val;
+ eat(state, TOK_EQ);
+ val = constant_expr(state);
+ integral(state, val);
+ base = val->u.cval;
+ }
+ value = int_const(state, &int_type, base);
+ symbol(state, eident, &eident->sym_ident, value, &int_type);
+ entry = new_type(TYPE_LIST, 0, 0);
+ entry->field_ident = eident;
+ *next = entry;
+ next = &entry->right;
+ base += 1;
+ if (peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ }
+ } while(peek(state) != TOK_RBRACE);
+ eat(state, TOK_RBRACE);
+ if (ident) {
+ symbol(state, ident, &ident->sym_tag, 0, enum_type);
+ }
+ }
+ if (ident && ident->sym_tag &&
+ ident->sym_tag->type &&
+ ((ident->sym_tag->type->type & TYPE_MASK) == TYPE_ENUM)) {
+ enum_type = clone_type(spec, ident->sym_tag->type);
+ }
+ else if (ident && !enum_type) {
+ error(state, 0, "enum %s undeclared", ident->name);
+ }
+ return enum_type;
+}
+
+static struct type *struct_declarator(
+ struct compile_state *state, struct type *type, struct hash_entry **ident)
+{
+ if (peek(state) != TOK_COLON) {
+ type = declarator(state, type, ident, 1);
+ }
+ if (peek(state) == TOK_COLON) {
+ struct triple *value;
+ eat(state, TOK_COLON);
+ value = constant_expr(state);
+ if (value->op != OP_INTCONST) {
+ error(state, 0, "Invalid constant expression");
+ }
+ if (value->u.cval > size_of(state, type)) {
+ error(state, 0, "bitfield larger than base type");
+ }
+ if (!TYPE_INTEGER(type->type) || ((type->type & TYPE_MASK) == TYPE_BITFIELD)) {
+ error(state, 0, "bitfield base not an integer type");
+ }
+ type = new_type(TYPE_BITFIELD, type, 0);
+ type->elements = value->u.cval;
+ }
+ return type;
+}
+
+static struct type *struct_or_union_specifier(
+ struct compile_state *state, unsigned int spec)
+{
+ struct type *struct_type;
+ struct hash_entry *ident;
+ unsigned int type_main;
+ unsigned int type_join;
+ int tok;
+ struct_type = 0;
+ ident = 0;
+ switch(peek(state)) {
+ case TOK_STRUCT:
+ eat(state, TOK_STRUCT);
+ type_main = TYPE_STRUCT;
+ type_join = TYPE_PRODUCT;
+ break;
+ case TOK_UNION:
+ eat(state, TOK_UNION);
+ type_main = TYPE_UNION;
+ type_join = TYPE_OVERLAP;
+ break;
+ default:
+ eat(state, TOK_STRUCT);
+ type_main = TYPE_STRUCT;
+ type_join = TYPE_PRODUCT;
+ break;
+ }
+ tok = peek(state);
+ if ((tok == TOK_IDENT) || (tok == TOK_ENUM_CONST) || (tok == TOK_TYPE_NAME)) {
+ ident = eat(state, tok)->ident;
+ }
+ if (!ident || (peek(state) == TOK_LBRACE)) {
+ ulong_t elements;
+ struct type **next;
+ elements = 0;
+ eat(state, TOK_LBRACE);
+ next = &struct_type;
+ do {
+ struct type *base_type;
+ int done;
+ base_type = specifier_qualifier_list(state);
+ do {
+ struct type *type;
+ struct hash_entry *fident;
+ done = 1;
+ type = struct_declarator(state, base_type, &fident);
+ elements++;
+ if (peek(state) == TOK_COMMA) {
+ done = 0;
+ eat(state, TOK_COMMA);
+ }
+ type = clone_type(0, type);
+ type->field_ident = fident;
+ if (*next) {
+ *next = new_type(type_join, *next, type);
+ next = &((*next)->right);
+ } else {
+ *next = type;
+ }
+ } while(!done);
+ eat(state, TOK_SEMI);
+ } while(peek(state) != TOK_RBRACE);
+ eat(state, TOK_RBRACE);
+ struct_type = new_type(type_main | spec, struct_type, 0);
+ struct_type->type_ident = ident;
+ struct_type->elements = elements;
+ if (ident) {
+ symbol(state, ident, &ident->sym_tag, 0, struct_type);
+ }
+ }
+ if (ident && ident->sym_tag &&
+ ident->sym_tag->type &&
+ ((ident->sym_tag->type->type & TYPE_MASK) == type_main)) {
+ struct_type = clone_type(spec, ident->sym_tag->type);
+ }
+ else if (ident && !struct_type) {
+ error(state, 0, "%s %s undeclared",
+ (type_main == TYPE_STRUCT)?"struct" : "union",
+ ident->name);
+ }
+ return struct_type;
+}
+
+static unsigned int storage_class_specifier_opt(struct compile_state *state)
+{
+ unsigned int specifiers;
+ switch(peek(state)) {
+ case TOK_AUTO:
+ eat(state, TOK_AUTO);
+ specifiers = STOR_AUTO;
+ break;
+ case TOK_REGISTER:
+ eat(state, TOK_REGISTER);
+ specifiers = STOR_REGISTER;
+ break;
+ case TOK_STATIC:
+ eat(state, TOK_STATIC);
+ specifiers = STOR_STATIC;
+ break;
+ case TOK_EXTERN:
+ eat(state, TOK_EXTERN);
+ specifiers = STOR_EXTERN;
+ break;
+ case TOK_TYPEDEF:
+ eat(state, TOK_TYPEDEF);
+ specifiers = STOR_TYPEDEF;
+ break;
+ default:
+ if (state->scope_depth <= GLOBAL_SCOPE_DEPTH) {
+ specifiers = STOR_LOCAL;
+ }
+ else {
+ specifiers = STOR_AUTO;
+ }
+ }
+ return specifiers;
+}
+
+static unsigned int function_specifier_opt(struct compile_state *state)
+{
+ /* Ignore the inline keyword */
+ unsigned int specifiers;
+ specifiers = 0;
+ switch(peek(state)) {
+ case TOK_INLINE:
+ eat(state, TOK_INLINE);
+ specifiers = STOR_INLINE;
+ }
+ return specifiers;
+}
+
+static unsigned int attrib(struct compile_state *state, unsigned int attributes)
+{
+ int tok = peek(state);
+ switch(tok) {
+ case TOK_COMMA:
+ case TOK_LPAREN:
+ /* The empty attribute ignore it */
+ break;
+ case TOK_IDENT:
+ case TOK_ENUM_CONST:
+ case TOK_TYPE_NAME:
+ {
+ struct hash_entry *ident;
+ ident = eat(state, TOK_IDENT)->ident;
+
+ if (ident == state->i_noinline) {
+ if (attributes & ATTRIB_ALWAYS_INLINE) {
+ error(state, 0, "both always_inline and noinline attribtes");
+ }
+ attributes |= ATTRIB_NOINLINE;
+ }
+ else if (ident == state->i_always_inline) {
+ if (attributes & ATTRIB_NOINLINE) {
+ error(state, 0, "both noinline and always_inline attribtes");
+ }
+ attributes |= ATTRIB_ALWAYS_INLINE;
+ }
+ else if (ident == state->i_noreturn) {
+ // attribute((noreturn)) does nothing (yet?)
+ }
+ else {
+ error(state, 0, "Unknown attribute:%s", ident->name);
+ }
+ break;
+ }
+ default:
+ error(state, 0, "Unexpected token: %s\n", tokens[tok]);
+ break;
+ }
+ return attributes;
+}
+
+static unsigned int attribute_list(struct compile_state *state, unsigned type)
+{
+ type = attrib(state, type);
+ while(peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ type = attrib(state, type);
+ }
+ return type;
+}
+
+static unsigned int attributes_opt(struct compile_state *state, unsigned type)
+{
+ if (peek(state) == TOK_ATTRIBUTE) {
+ eat(state, TOK_ATTRIBUTE);
+ eat(state, TOK_LPAREN);
+ eat(state, TOK_LPAREN);
+ type = attribute_list(state, type);
+ eat(state, TOK_RPAREN);
+ eat(state, TOK_RPAREN);
+ }
+ return type;
+}
+
+static unsigned int type_qualifiers(struct compile_state *state)
+{
+ unsigned int specifiers;
+ int done;
+ done = 0;
+ specifiers = QUAL_NONE;
+ do {
+ switch(peek(state)) {
+ case TOK_CONST:
+ eat(state, TOK_CONST);
+ specifiers |= QUAL_CONST;
+ break;
+ case TOK_VOLATILE:
+ eat(state, TOK_VOLATILE);
+ specifiers |= QUAL_VOLATILE;
+ break;
+ case TOK_RESTRICT:
+ eat(state, TOK_RESTRICT);
+ specifiers |= QUAL_RESTRICT;
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ } while(!done);
+ return specifiers;
+}
+
+static struct type *type_specifier(
+ struct compile_state *state, unsigned int spec)
+{
+ struct type *type;
+ int tok;
+ type = 0;
+ switch((tok = peek(state))) {
+ case TOK_VOID:
+ eat(state, TOK_VOID);
+ type = new_type(TYPE_VOID | spec, 0, 0);
+ break;
+ case TOK_CHAR:
+ eat(state, TOK_CHAR);
+ type = new_type(TYPE_CHAR | spec, 0, 0);
+ break;
+ case TOK_SHORT:
+ eat(state, TOK_SHORT);
+ if (peek(state) == TOK_INT) {
+ eat(state, TOK_INT);
+ }
+ type = new_type(TYPE_SHORT | spec, 0, 0);
+ break;
+ case TOK_INT:
+ eat(state, TOK_INT);
+ type = new_type(TYPE_INT | spec, 0, 0);
+ break;
+ case TOK_LONG:
+ eat(state, TOK_LONG);
+ switch(peek(state)) {
+ case TOK_LONG:
+ eat(state, TOK_LONG);
+ error(state, 0, "long long not supported");
+ break;
+ case TOK_DOUBLE:
+ eat(state, TOK_DOUBLE);
+ error(state, 0, "long double not supported");
+ break;
+ case TOK_INT:
+ eat(state, TOK_INT);
+ type = new_type(TYPE_LONG | spec, 0, 0);
+ break;
+ default:
+ type = new_type(TYPE_LONG | spec, 0, 0);
+ break;
+ }
+ break;
+ case TOK_FLOAT:
+ eat(state, TOK_FLOAT);
+ error(state, 0, "type float not supported");
+ break;
+ case TOK_DOUBLE:
+ eat(state, TOK_DOUBLE);
+ error(state, 0, "type double not supported");
+ break;
+ case TOK_SIGNED:
+ eat(state, TOK_SIGNED);
+ switch(peek(state)) {
+ case TOK_LONG:
+ eat(state, TOK_LONG);
+ switch(peek(state)) {
+ case TOK_LONG:
+ eat(state, TOK_LONG);
+ error(state, 0, "type long long not supported");
+ break;
+ case TOK_INT:
+ eat(state, TOK_INT);
+ type = new_type(TYPE_LONG | spec, 0, 0);
+ break;
+ default:
+ type = new_type(TYPE_LONG | spec, 0, 0);
+ break;
+ }
+ break;
+ case TOK_INT:
+ eat(state, TOK_INT);
+ type = new_type(TYPE_INT | spec, 0, 0);
+ break;
+ case TOK_SHORT:
+ eat(state, TOK_SHORT);
+ type = new_type(TYPE_SHORT | spec, 0, 0);
+ break;
+ case TOK_CHAR:
+ eat(state, TOK_CHAR);
+ type = new_type(TYPE_CHAR | spec, 0, 0);
+ break;
+ default:
+ type = new_type(TYPE_INT | spec, 0, 0);
+ break;
+ }
+ break;
+ case TOK_UNSIGNED:
+ eat(state, TOK_UNSIGNED);
+ switch(peek(state)) {
+ case TOK_LONG:
+ eat(state, TOK_LONG);
+ switch(peek(state)) {
+ case TOK_LONG:
+ eat(state, TOK_LONG);
+ error(state, 0, "unsigned long long not supported");
+ break;
+ case TOK_INT:
+ eat(state, TOK_INT);
+ type = new_type(TYPE_ULONG | spec, 0, 0);
+ break;
+ default:
+ type = new_type(TYPE_ULONG | spec, 0, 0);
+ break;
+ }
+ break;
+ case TOK_INT:
+ eat(state, TOK_INT);
+ type = new_type(TYPE_UINT | spec, 0, 0);
+ break;
+ case TOK_SHORT:
+ eat(state, TOK_SHORT);
+ type = new_type(TYPE_USHORT | spec, 0, 0);
+ break;
+ case TOK_CHAR:
+ eat(state, TOK_CHAR);
+ type = new_type(TYPE_UCHAR | spec, 0, 0);
+ break;
+ default:
+ type = new_type(TYPE_UINT | spec, 0, 0);
+ break;
+ }
+ break;
+ /* struct or union specifier */
+ case TOK_STRUCT:
+ case TOK_UNION:
+ type = struct_or_union_specifier(state, spec);
+ break;
+ /* enum-spefifier */
+ case TOK_ENUM:
+ type = enum_specifier(state, spec);
+ break;
+ /* typedef name */
+ case TOK_TYPE_NAME:
+ type = typedef_name(state, spec);
+ break;
+ default:
+ error(state, 0, "bad type specifier %s",
+ tokens[tok]);
+ break;
+ }
+ return type;
+}
+
+static int istype(int tok)
+{
+ switch(tok) {
+ case TOK_CONST:
+ case TOK_RESTRICT:
+ case TOK_VOLATILE:
+ case TOK_VOID:
+ case TOK_CHAR:
+ case TOK_SHORT:
+ case TOK_INT:
+ case TOK_LONG:
+ case TOK_FLOAT:
+ case TOK_DOUBLE:
+ case TOK_SIGNED:
+ case TOK_UNSIGNED:
+ case TOK_STRUCT:
+ case TOK_UNION:
+ case TOK_ENUM:
+ case TOK_TYPE_NAME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+static struct type *specifier_qualifier_list(struct compile_state *state)
+{
+ struct type *type;
+ unsigned int specifiers = 0;
+
+ /* type qualifiers */
+ specifiers |= type_qualifiers(state);
+
+ /* type specifier */
+ type = type_specifier(state, specifiers);
+
+ return type;
+}
+
+#if DEBUG_ROMCC_WARNING
+static int isdecl_specifier(int tok)
+{
+ switch(tok) {
+ /* storage class specifier */
+ case TOK_AUTO:
+ case TOK_REGISTER:
+ case TOK_STATIC:
+ case TOK_EXTERN:
+ case TOK_TYPEDEF:
+ /* type qualifier */
+ case TOK_CONST:
+ case TOK_RESTRICT:
+ case TOK_VOLATILE:
+ /* type specifiers */
+ case TOK_VOID:
+ case TOK_CHAR:
+ case TOK_SHORT:
+ case TOK_INT:
+ case TOK_LONG:
+ case TOK_FLOAT:
+ case TOK_DOUBLE:
+ case TOK_SIGNED:
+ case TOK_UNSIGNED:
+ /* struct or union specifier */
+ case TOK_STRUCT:
+ case TOK_UNION:
+ /* enum-spefifier */
+ case TOK_ENUM:
+ /* typedef name */
+ case TOK_TYPE_NAME:
+ /* function specifiers */
+ case TOK_INLINE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+#endif
+
+static struct type *decl_specifiers(struct compile_state *state)
+{
+ struct type *type;
+ unsigned int specifiers;
+ /* I am overly restrictive in the arragement of specifiers supported.
+ * C is overly flexible in this department it makes interpreting
+ * the parse tree difficult.
+ */
+ specifiers = 0;
+
+ /* storage class specifier */
+ specifiers |= storage_class_specifier_opt(state);
+
+ /* function-specifier */
+ specifiers |= function_specifier_opt(state);
+
+ /* attributes */
+ specifiers |= attributes_opt(state, 0);
+
+ /* type qualifier */
+ specifiers |= type_qualifiers(state);
+
+ /* type specifier */
+ type = type_specifier(state, specifiers);
+ return type;
+}
+
+struct field_info {
+ struct type *type;
+ size_t offset;
+};
+
+static struct field_info designator(struct compile_state *state, struct type *type)
+{
+ int tok;
+ struct field_info info;
+ info.offset = ~0U;
+ info.type = 0;
+ do {
+ switch(peek(state)) {
+ case TOK_LBRACKET:
+ {
+ struct triple *value;
+ if ((type->type & TYPE_MASK) != TYPE_ARRAY) {
+ error(state, 0, "Array designator not in array initializer");
+ }
+ eat(state, TOK_LBRACKET);
+ value = constant_expr(state);
+ eat(state, TOK_RBRACKET);
+
+ info.type = type->left;
+ info.offset = value->u.cval * size_of(state, info.type);
+ break;
+ }
+ case TOK_DOT:
+ {
+ struct hash_entry *field;
+ if (((type->type & TYPE_MASK) != TYPE_STRUCT) &&
+ ((type->type & TYPE_MASK) != TYPE_UNION))
+ {
+ error(state, 0, "Struct designator not in struct initializer");
+ }
+ eat(state, TOK_DOT);
+ field = eat(state, TOK_IDENT)->ident;
+ info.offset = field_offset(state, type, field);
+ info.type = field_type(state, type, field);
+ break;
+ }
+ default:
+ error(state, 0, "Invalid designator");
+ }
+ tok = peek(state);
+ } while((tok == TOK_LBRACKET) || (tok == TOK_DOT));
+ eat(state, TOK_EQ);
+ return info;
+}
+
+static struct triple *initializer(
+ struct compile_state *state, struct type *type)
+{
+ struct triple *result;
+#if DEBUG_ROMCC_WARNINGS
+#warning "FIXME more consistent initializer handling (where should eval_const_expr go?"
+#endif
+ if (peek(state) != TOK_LBRACE) {
+ result = assignment_expr(state);
+ if (((type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ (type->elements == ELEMENT_COUNT_UNSPECIFIED) &&
+ ((result->type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ (result->type->elements != ELEMENT_COUNT_UNSPECIFIED) &&
+ (equiv_types(type->left, result->type->left))) {
+ type->elements = result->type->elements;
+ }
+ if (is_lvalue(state, result) &&
+ ((result->type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ (type->type & TYPE_MASK) != TYPE_ARRAY)
+ {
+ result = lvalue_conversion(state, result);
+ }
+ if (!is_init_compatible(state, type, result->type)) {
+ error(state, 0, "Incompatible types in initializer");
+ }
+ if (!equiv_types(type, result->type)) {
+ result = mk_cast_expr(state, type, result);
+ }
+ }
+ else {
+ int comma;
+ size_t max_offset;
+ struct field_info info;
+ void *buf;
+ if (((type->type & TYPE_MASK) != TYPE_ARRAY) &&
+ ((type->type & TYPE_MASK) != TYPE_STRUCT)) {
+ internal_error(state, 0, "unknown initializer type");
+ }
+ info.offset = 0;
+ info.type = type->left;
+ if ((type->type & TYPE_MASK) == TYPE_STRUCT) {
+ info.type = next_field(state, type, 0);
+ }
+ if (type->elements == ELEMENT_COUNT_UNSPECIFIED) {
+ max_offset = 0;
+ } else {
+ max_offset = size_of(state, type);
+ }
+ buf = xcmalloc(bits_to_bytes(max_offset), "initializer");
+ eat(state, TOK_LBRACE);
+ do {
+ struct triple *value;
+ struct type *value_type;
+ size_t value_size;
+ void *dest;
+ int tok;
+ comma = 0;
+ tok = peek(state);
+ if ((tok == TOK_LBRACKET) || (tok == TOK_DOT)) {
+ info = designator(state, type);
+ }
+ if ((type->elements != ELEMENT_COUNT_UNSPECIFIED) &&
+ (info.offset >= max_offset)) {
+ error(state, 0, "element beyond bounds");
+ }
+ value_type = info.type;
+ value = eval_const_expr(state, initializer(state, value_type));
+ value_size = size_of(state, value_type);
+ if (((type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ (type->elements == ELEMENT_COUNT_UNSPECIFIED) &&
+ (max_offset <= info.offset)) {
+ void *old_buf;
+ size_t old_size;
+ old_buf = buf;
+ old_size = max_offset;
+ max_offset = info.offset + value_size;
+ buf = xmalloc(bits_to_bytes(max_offset), "initializer");
+ memcpy(buf, old_buf, bits_to_bytes(old_size));
+ xfree(old_buf);
+ }
+ dest = ((char *)buf) + bits_to_bytes(info.offset);
+#if DEBUG_INITIALIZER
+ fprintf(state->errout, "dest = buf + %d max_offset: %d value_size: %d op: %d\n",
+ dest - buf,
+ bits_to_bytes(max_offset),
+ bits_to_bytes(value_size),
+ value->op);
+#endif
+ if (value->op == OP_BLOBCONST) {
+ memcpy(dest, value->u.blob, bits_to_bytes(value_size));
+ }
+ else if ((value->op == OP_INTCONST) && (value_size == SIZEOF_I8)) {
+#if DEBUG_INITIALIZER
+ fprintf(state->errout, "byte: %02x\n", value->u.cval & 0xff);
+#endif
+ *((uint8_t *)dest) = value->u.cval & 0xff;
+ }
+ else if ((value->op == OP_INTCONST) && (value_size == SIZEOF_I16)) {
+ *((uint16_t *)dest) = value->u.cval & 0xffff;
+ }
+ else if ((value->op == OP_INTCONST) && (value_size == SIZEOF_I32)) {
+ *((uint32_t *)dest) = value->u.cval & 0xffffffff;
+ }
+ else {
+ internal_error(state, 0, "unhandled constant initializer");
+ }
+ free_triple(state, value);
+ if (peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ comma = 1;
+ }
+ info.offset += value_size;
+ if ((type->type & TYPE_MASK) == TYPE_STRUCT) {
+ info.type = next_field(state, type, info.type);
+ info.offset = field_offset(state, type,
+ info.type->field_ident);
+ }
+ } while(comma && (peek(state) != TOK_RBRACE));
+ if ((type->elements == ELEMENT_COUNT_UNSPECIFIED) &&
+ ((type->type & TYPE_MASK) == TYPE_ARRAY)) {
+ type->elements = max_offset / size_of(state, type->left);
+ }
+ eat(state, TOK_RBRACE);
+ result = triple(state, OP_BLOBCONST, type, 0, 0);
+ result->u.blob = buf;
+ }
+ return result;
+}
+
+static void resolve_branches(struct compile_state *state, struct triple *first)
+{
+ /* Make a second pass and finish anything outstanding
+ * with respect to branches. The only outstanding item
+ * is to see if there are goto to labels that have not
+ * been defined and to error about them.
+ */
+ int i;
+ struct triple *ins;
+ /* Also error on branches that do not use their targets */
+ ins = first;
+ do {
+ if (!triple_is_ret(state, ins)) {
+ struct triple **expr ;
+ struct triple_set *set;
+ expr = triple_targ(state, ins, 0);
+ for(; expr; expr = triple_targ(state, ins, expr)) {
+ struct triple *targ;
+ targ = *expr;
+ for(set = targ?targ->use:0; set; set = set->next) {
+ if (set->member == ins) {
+ break;
+ }
+ }
+ if (!set) {
+ internal_error(state, ins, "targ not used");
+ }
+ }
+ }
+ ins = ins->next;
+ } while(ins != first);
+ /* See if there are goto to labels that have not been defined */
+ for(i = 0; i < HASH_TABLE_SIZE; i++) {
+ struct hash_entry *entry;
+ for(entry = state->hash_table[i]; entry; entry = entry->next) {
+ struct triple *ins;
+ if (!entry->sym_label) {
+ continue;
+ }
+ ins = entry->sym_label->def;
+ if (!(ins->id & TRIPLE_FLAG_FLATTENED)) {
+ error(state, ins, "label `%s' used but not defined",
+ entry->name);
+ }
+ }
+ }
+}
+
+static struct triple *function_definition(
+ struct compile_state *state, struct type *type)
+{
+ struct triple *def, *tmp, *first, *end, *retvar, *ret;
+ struct triple *fname;
+ struct type *fname_type;
+ struct hash_entry *ident;
+ struct type *param, *crtype, *ctype;
+ int i;
+ if ((type->type &TYPE_MASK) != TYPE_FUNCTION) {
+ error(state, 0, "Invalid function header");
+ }
+
+ /* Verify the function type */
+ if (((type->right->type & TYPE_MASK) != TYPE_VOID) &&
+ ((type->right->type & TYPE_MASK) != TYPE_PRODUCT) &&
+ (type->right->field_ident == 0)) {
+ error(state, 0, "Invalid function parameters");
+ }
+ param = type->right;
+ i = 0;
+ while((param->type & TYPE_MASK) == TYPE_PRODUCT) {
+ i++;
+ if (!param->left->field_ident) {
+ error(state, 0, "No identifier for parameter %d\n", i);
+ }
+ param = param->right;
+ }
+ i++;
+ if (((param->type & TYPE_MASK) != TYPE_VOID) && !param->field_ident) {
+ error(state, 0, "No identifier for paramter %d\n", i);
+ }
+
+ /* Get a list of statements for this function. */
+ def = triple(state, OP_LIST, type, 0, 0);
+
+ /* Start a new scope for the passed parameters */
+ start_scope(state);
+
+ /* Put a label at the very start of a function */
+ first = label(state);
+ RHS(def, 0) = first;
+
+ /* Put a label at the very end of a function */
+ end = label(state);
+ flatten(state, first, end);
+ /* Remember where return goes */
+ ident = state->i_return;
+ symbol(state, ident, &ident->sym_ident, end, end->type);
+
+ /* Get the initial closure type */
+ ctype = new_type(TYPE_JOIN, &void_type, 0);
+ ctype->elements = 1;
+
+ /* Add a variable for the return value */
+ crtype = new_type(TYPE_TUPLE,
+ /* Remove all type qualifiers from the return type */
+ new_type(TYPE_PRODUCT, ctype, clone_type(0, type->left)), 0);
+ crtype->elements = 2;
+ flatten(state, end, variable(state, crtype));
+
+ /* Allocate a variable for the return address */
+ retvar = flatten(state, end, variable(state, &void_ptr_type));
+
+ /* Add in the return instruction */
+ ret = triple(state, OP_RET, &void_type, read_expr(state, retvar), 0);
+ ret = flatten(state, first, ret);
+
+ /* Walk through the parameters and create symbol table entries
+ * for them.
+ */
+ param = type->right;
+ while((param->type & TYPE_MASK) == TYPE_PRODUCT) {
+ ident = param->left->field_ident;
+ tmp = variable(state, param->left);
+ var_symbol(state, ident, tmp);
+ flatten(state, end, tmp);
+ param = param->right;
+ }
+ if ((param->type & TYPE_MASK) != TYPE_VOID) {
+ /* And don't forget the last parameter */
+ ident = param->field_ident;
+ tmp = variable(state, param);
+ symbol(state, ident, &ident->sym_ident, tmp, tmp->type);
+ flatten(state, end, tmp);
+ }
+
+ /* Add the declaration static const char __func__ [] = "func-name" */
+ fname_type = new_type(TYPE_ARRAY,
+ clone_type(QUAL_CONST | STOR_STATIC, &char_type), 0);
+ fname_type->type |= QUAL_CONST | STOR_STATIC;
+ fname_type->elements = strlen(state->function) + 1;
+
+ fname = triple(state, OP_BLOBCONST, fname_type, 0, 0);
+ fname->u.blob = (void *)state->function;
+ fname = flatten(state, end, fname);
+
+ ident = state->i___func__;
+ symbol(state, ident, &ident->sym_ident, fname, fname_type);
+
+ /* Remember which function I am compiling.
+ * Also assume the last defined function is the main function.
+ */
+ state->main_function = def;
+
+ /* Now get the actual function definition */
+ compound_statement(state, end);
+
+ /* Finish anything unfinished with branches */
+ resolve_branches(state, first);
+
+ /* Remove the parameter scope */
+ end_scope(state);
+
+
+ /* Remember I have defined a function */
+ if (!state->functions) {
+ state->functions = def;
+ } else {
+ insert_triple(state, state->functions, def);
+ }
+ if (state->compiler->debug & DEBUG_INLINE) {
+ FILE *fp = state->dbgout;
+ fprintf(fp, "\n");
+ loc(fp, state, 0);
+ fprintf(fp, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(state, fp, def);
+ fprintf(fp, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ return def;
+}
+
+static struct triple *do_decl(struct compile_state *state,
+ struct type *type, struct hash_entry *ident)
+{
+ struct triple *def;
+ def = 0;
+ /* Clean up the storage types used */
+ switch (type->type & STOR_MASK) {
+ case STOR_AUTO:
+ case STOR_STATIC:
+ /* These are the good types I am aiming for */
+ break;
+ case STOR_REGISTER:
+ type->type &= ~STOR_MASK;
+ type->type |= STOR_AUTO;
+ break;
+ case STOR_LOCAL:
+ case STOR_EXTERN:
+ type->type &= ~STOR_MASK;
+ type->type |= STOR_STATIC;
+ break;
+ case STOR_TYPEDEF:
+ if (!ident) {
+ error(state, 0, "typedef without name");
+ }
+ symbol(state, ident, &ident->sym_ident, 0, type);
+ ident->tok = TOK_TYPE_NAME;
+ return 0;
+ break;
+ default:
+ internal_error(state, 0, "Undefined storage class");
+ }
+ if ((type->type & TYPE_MASK) == TYPE_FUNCTION) {
+ error(state, 0, "Function prototypes not supported");
+ }
+ if (ident &&
+ ((type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ ((type->type & STOR_MASK) != STOR_STATIC))
+ error(state, 0, "non static arrays not supported");
+ if (ident &&
+ ((type->type & STOR_MASK) == STOR_STATIC) &&
+ ((type->type & QUAL_CONST) == 0)) {
+ error(state, 0, "non const static variables not supported");
+ }
+ if (ident) {
+ def = variable(state, type);
+ var_symbol(state, ident, def);
+ }
+ return def;
+}
+
+static void decl(struct compile_state *state, struct triple *first)
+{
+ struct type *base_type, *type;
+ struct hash_entry *ident;
+ struct triple *def;
+ int global;
+ global = (state->scope_depth <= GLOBAL_SCOPE_DEPTH);
+ base_type = decl_specifiers(state);
+ ident = 0;
+ type = declarator(state, base_type, &ident, 0);
+ type->type = attributes_opt(state, type->type);
+ if (global && ident && (peek(state) == TOK_LBRACE)) {
+ /* function */
+ type->type_ident = ident;
+ state->function = ident->name;
+ def = function_definition(state, type);
+ symbol(state, ident, &ident->sym_ident, def, type);
+ state->function = 0;
+ }
+ else {
+ int done;
+ flatten(state, first, do_decl(state, type, ident));
+ /* type or variable definition */
+ do {
+ done = 1;
+ if (peek(state) == TOK_EQ) {
+ if (!ident) {
+ error(state, 0, "cannot assign to a type");
+ }
+ eat(state, TOK_EQ);
+ flatten(state, first,
+ init_expr(state,
+ ident->sym_ident->def,
+ initializer(state, type)));
+ }
+ arrays_complete(state, type);
+ if (peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ ident = 0;
+ type = declarator(state, base_type, &ident, 0);
+ flatten(state, first, do_decl(state, type, ident));
+ done = 0;
+ }
+ } while(!done);
+ eat(state, TOK_SEMI);
+ }
+}
+
+static void decls(struct compile_state *state)
+{
+ struct triple *list;
+ int tok;
+ list = label(state);
+ while(1) {
+ tok = peek(state);
+ if (tok == TOK_EOF) {
+ return;
+ }
+ if (tok == TOK_SPACE) {
+ eat(state, TOK_SPACE);
+ }
+ decl(state, list);
+ if (list->next != list) {
+ error(state, 0, "global variables not supported");
+ }
+ }
+}
+
+/*
+ * Function inlining
+ */
+struct triple_reg_set {
+ struct triple_reg_set *next;
+ struct triple *member;
+ struct triple *new;
+};
+struct reg_block {
+ struct block *block;
+ struct triple_reg_set *in;
+ struct triple_reg_set *out;
+ int vertex;
+};
+static void setup_basic_blocks(struct compile_state *, struct basic_blocks *bb);
+static void analyze_basic_blocks(struct compile_state *state, struct basic_blocks *bb);
+static void free_basic_blocks(struct compile_state *, struct basic_blocks *bb);
+static int tdominates(struct compile_state *state, struct triple *dom, struct triple *sub);
+static void walk_blocks(struct compile_state *state, struct basic_blocks *bb,
+ void (*cb)(struct compile_state *state, struct block *block, void *arg),
+ void *arg);
+static void print_block(
+ struct compile_state *state, struct block *block, void *arg);
+static int do_triple_set(struct triple_reg_set **head,
+ struct triple *member, struct triple *new_member);
+static void do_triple_unset(struct triple_reg_set **head, struct triple *member);
+static struct reg_block *compute_variable_lifetimes(
+ struct compile_state *state, struct basic_blocks *bb);
+static void free_variable_lifetimes(struct compile_state *state,
+ struct basic_blocks *bb, struct reg_block *blocks);
+#if DEBUG_EXPLICIT_CLOSURES
+static void print_live_variables(struct compile_state *state,
+ struct basic_blocks *bb, struct reg_block *rb, FILE *fp);
+#endif
+
+
+static struct triple *call(struct compile_state *state,
+ struct triple *retvar, struct triple *ret_addr,
+ struct triple *targ, struct triple *ret)
+{
+ struct triple *call;
+
+ if (!retvar || !is_lvalue(state, retvar)) {
+ internal_error(state, 0, "writing to a non lvalue?");
+ }
+ write_compatible(state, retvar->type, &void_ptr_type);
+
+ call = new_triple(state, OP_CALL, &void_type, 1, 0);
+ TARG(call, 0) = targ;
+ MISC(call, 0) = ret;
+ if (!targ || (targ->op != OP_LABEL)) {
+ internal_error(state, 0, "call not to a label");
+ }
+ if (!ret || (ret->op != OP_RET)) {
+ internal_error(state, 0, "call not matched with return");
+ }
+ return call;
+}
+
+static void walk_functions(struct compile_state *state,
+ void (*cb)(struct compile_state *state, struct triple *func, void *arg),
+ void *arg)
+{
+ struct triple *func, *first;
+ func = first = state->functions;
+ do {
+ cb(state, func, arg);
+ func = func->next;
+ } while(func != first);
+}
+
+static void reverse_walk_functions(struct compile_state *state,
+ void (*cb)(struct compile_state *state, struct triple *func, void *arg),
+ void *arg)
+{
+ struct triple *func, *first;
+ func = first = state->functions;
+ do {
+ func = func->prev;
+ cb(state, func, arg);
+ } while(func != first);
+}
+
+
+static void mark_live(struct compile_state *state, struct triple *func, void *arg)
+{
+ struct triple *ptr, *first;
+ if (func->u.cval == 0) {
+ return;
+ }
+ ptr = first = RHS(func, 0);
+ do {
+ if (ptr->op == OP_FCALL) {
+ struct triple *called_func;
+ called_func = MISC(ptr, 0);
+ /* Mark the called function as used */
+ if (!(func->id & TRIPLE_FLAG_FLATTENED)) {
+ called_func->u.cval++;