diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 77 |
1 files changed, 55 insertions, 22 deletions
@@ -217,35 +217,68 @@ Node *node_new_lit_i64(Proc *p, int64_t i) { return n; } +static inline int node_op_communative(NodeType t) { + NodeType ops[] = { N_OP_ADD, N_OP_MUL, N_OP_AND, N_OP_XOR, N_OP_OR }; + for (unsigned i = 0; i < sizeof ops / sizeof *ops; i++) { + if (ops[i] == t) return 1; + } + return 0; +} + /* needs lexer for error reporting */ Node *node_peephole(Node *n, Proc *p, Lexer *l) { return n; - for (int i = 0; i < n->in.len; i++) { - if (n->in.data[i]->type != N_LIT) return n; - } + /* + * Is there a method to convey these transformations a little more nicely? + * + * add(lit, lit) -> lit + * add(lit, add(N, lit)) -> add(N, add(lit, lit)) + * + */ + Node **in = n->in.data; Node *r = n; - switch (n->type) { - case N_OP_NEG: r = node_new_lit_i64(p, -in[0]->lit.i); break; - case N_OP_NOT: r = node_new_lit_i64(p, ~in[0]->lit.i); break; - case N_OP_ADD: r = node_new_lit_i64(p, in[0]->lit.i + in[1]->lit.i); break; - case N_OP_SUB: r = node_new_lit_i64(p, in[0]->lit.i - in[1]->lit.i); break; - case N_OP_MUL: r = node_new_lit_i64(p, in[0]->lit.i * in[1]->lit.i); break; - case N_OP_DIV: - if (in[1]->lit.i == 0) { - lex_error_at(l, n->src_pos, LE_ERROR, S("Division by zero")); + + int all_lit = 1; + for (int i = 0; i < n->in.len; i++) { + if (n->in.data[i]->type != N_LIT) all_lit = 0; + } + + if (all_lit) { + switch (n->type) { + case N_OP_NEG: r = node_new_lit_i64(p, -in[0]->lit.i); break; + case N_OP_NOT: r = node_new_lit_i64(p, ~in[0]->lit.i); break; + case N_OP_ADD: r = node_new_lit_i64(p, in[0]->lit.i + in[1]->lit.i); break; + case N_OP_SUB: r = node_new_lit_i64(p, in[0]->lit.i - in[1]->lit.i); break; + case N_OP_MUL: r = node_new_lit_i64(p, in[0]->lit.i * in[1]->lit.i); break; + case N_OP_DIV: + if (in[1]->lit.i == 0) { + lex_error_at(l, n->src_pos, LE_ERROR, S("Division by zero")); + } + r = node_new_lit_i64(p, in[0]->lit.i / in[1]->lit.i); + break; + case N_OP_AND: r = node_new_lit_i64(p, in[0]->lit.i & in[1]->lit.i); break; + case N_OP_OR: r = node_new_lit_i64(p, in[0]->lit.i | in[1]->lit.i); break; + case N_OP_XOR: r = node_new_lit_i64(p, in[0]->lit.i ^ in[1]->lit.i); break; + default: break; } - r = node_new_lit_i64(p, in[0]->lit.i / in[1]->lit.i); - break; - case N_OP_AND: r = node_new_lit_i64(p, in[0]->lit.i & in[1]->lit.i); break; - case N_OP_OR: r = node_new_lit_i64(p, in[0]->lit.i | in[1]->lit.i); break; - case N_OP_XOR: r = node_new_lit_i64(p, in[0]->lit.i ^ in[1]->lit.i); break; - default: - return n; - break; + } else { + if (node_op_communative(n->type) + && in[0]->type == N_LIT + && in[1]->type == n->type + && in[1]->in.data[0]->type != N_LIT + && in[1]->in.data[1]->type == N_LIT) { + /* op(lit, op(X, lit)) -> op(X, op(lit, lit)) */ + Node *tmp = in[1]->in.data[0]; + in[1]->in.data[0] = in[0]; + in[0] = tmp; + } + } + + if (r != n) { + r->src_pos = n->src_pos; + if (n->out.len == 0) node_kill(n, p); } - r->src_pos = n->src_pos; - if (n->out.len == 0) node_kill(n, p); return r; } |
