aboutsummaryrefslogtreecommitdiff
path: root/src/correct.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/correct.c')
-rw-r--r--src/correct.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/correct.c b/src/correct.c
new file mode 100644
index 0000000..2654218
--- /dev/null
+++ b/src/correct.c
@@ -0,0 +1,138 @@
+#include <qbt/opt.h>
+
+static size_t spill_ref(struct blk *b, size_t ii, struct insn i, size_t idx, size_t ri)
+{
+ struct val tmp = tmp_val(ri++);
+ struct insn new = insn_create(ADDR, I27, tmp, i.in[idx], noclass());
+ i.in[idx] = tmp;
+ insn_at(b->insns, ii) = i;
+ insn_insert(b, new, ii);
+ return ri;
+}
+
+static size_t spill_imm(struct blk *b, size_t ii, struct insn i, size_t idx, size_t ri)
+{
+ struct val tmp = tmp_val(ri++);
+ struct insn new = insn_create(COPY, I27, tmp, i.in[idx], noclass());
+ i.in[idx] = tmp;
+ insn_at(b->insns, ii) = i;
+ insn_insert(b, new, ii);
+ return ri;
+}
+
+static size_t correct_arith(struct blk *b, size_t ii, struct insn i, size_t ri)
+{
+ if (i.in[0].class == IMM) {
+ /* swap so that immediates are 'outermost' */
+ struct val tmp = i.in[0];
+ i.in[0] = i.in[1];
+ i.in[1] = tmp;
+ insn_at(b->insns, ii) = i;
+ return ri;
+ }
+
+ if (i.in[0].class == IMM)
+ return spill_imm(b, ii, i, 0, ri);
+
+ return ri;
+}
+
+static size_t correct_positional_arith(struct blk *b, size_t ii, struct insn i, size_t ri)
+{
+ if (i.in[0].class == IMM)
+ return spill_imm(b, ii, i, 0, ri);
+
+ return ri;
+}
+
+static size_t correct_branch(struct blk *b, size_t ii, struct insn i, size_t ri)
+{
+ if (i.in[0].class == IMM)
+ return spill_imm(b, ii, i, 0, ri);
+
+ if (i.in[1].class == IMM)
+ return spill_imm(b, ii, i, 1, ri);
+
+ return ri;
+}
+
+static size_t correct_relations(struct blk *b, size_t ii, struct insn i, size_t ri)
+{
+ if (i.in[0].class == IMM)
+ return spill_imm(b, ii, i, 0, ri);
+
+ return ri;
+}
+
+static size_t correct_insn(struct blk *b, size_t ii, struct insn i, size_t ri)
+{
+ /* replace references with instructions */
+ if (i.type != CALL && i.in[0].class == REF)
+ return spill_ref(b, ii, i, 0, ri);
+
+ if (i.in[1].class == REF)
+ return spill_ref(b, ii, i, 1, ri);
+
+ switch (i.type) {
+ case ADD:
+ case MUL:
+ return correct_arith(b, ii, i, ri);
+
+ case SUB:
+ case DIV:
+ case REM:
+ case LSHIFT:
+ case RSHIFT:
+ return correct_positional_arith(b, ii, i, ri);
+
+ case BEQ:
+ case BNE:
+ case BLE:
+ case BGE:
+ case BLT:
+ case BGT:
+ case BNZ:
+ case BEZ:
+ /* oh wait, this never triggers because branches are at the end
+ * of blocks, duh */
+ return correct_branch(b, ii, i, ri);
+
+ case LT:
+ case LE:
+ case GT:
+ case GE:
+ case EQ:
+ case NE:
+ return correct_relations(b, ii, i, ri);
+
+ default:
+ }
+
+ return ri;
+}
+
+void correct(struct fn *f, size_t ri)
+{
+ /* some simpler corrections to make sure all instructions follow a
+ * specific pattern. The textual version doesn't have these
+ * restrictions, but they make our lives easier in the future. */
+ foreach_blk(bi, f->blks) {
+ struct blk *b = blk_at(f->blks, bi);
+ foreach_insn(ii, b->insns) {
+ struct insn i = insn_at(b->insns, ii);
+ ri = correct_insn(b, ii, i, ri);
+ }
+
+ if (b->cmp[0].class == IMM) {
+ struct val t = tmp_val(ri++);
+ insadd(b, COPY, I27, t, b->cmp[0], noclass());
+ b->cmp[0] = t;
+ }
+
+ if (b->cmp[1].class == IMM) {
+ struct val t = tmp_val(ri++);
+ insadd(b, COPY, I27, t, b->cmp[0], noclass());
+ b->cmp[1] = t;
+ }
+ }
+}