aboutsummaryrefslogtreecommitdiff
path: root/src/regalloc.c
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-03-31 23:09:14 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-03-31 23:09:14 +0300
commit59148d666a78d671198d56caabe7fe4d7fb3fee1 (patch)
tree4c54666dca7090fd8d4f898160afe080ed0f1fa6 /src/regalloc.c
parent1dcaa90d8b706118235bc74a0f2cbdcb836d29bb (diff)
downloadqbt-59148d666a78d671198d56caabe7fe4d7fb3fee1.tar.gz
qbt-59148d666a78d671198d56caabe7fe4d7fb3fee1.zip
produce some very limited assembly output
+ Several subsystems missing critical features, but enough to do some basic things like calling procedures, printing to screen and doing loops
Diffstat (limited to 'src/regalloc.c')
-rw-r--r--src/regalloc.c90
1 files changed, 90 insertions, 0 deletions
diff --git a/src/regalloc.c b/src/regalloc.c
new file mode 100644
index 0000000..474e5a4
--- /dev/null
+++ b/src/regalloc.c
@@ -0,0 +1,90 @@
+#include <assert.h>
+
+#include <qbt/regalloc.h>
+#include <qbt/vec.h>
+#include <qbt/abi.h>
+
+static const int64_t tr_map[] = {
+ RS0, RS1, RS2, RS3, RS4, RS5, RS6, RS7, RS8, RS9,
+ RS10, RS11, RS12, RS13, RS14, RS15, RS16, RS17, RS18, RS19,
+ RS20, RS21, RS22, RS23, RS24
+};
+
+#define reg_at(v, i)\
+ vect_at(int64_t, v, i)
+
+static struct val rewrite_tmp(struct vec rmap, struct val t)
+{
+ assert(t.class == TMP);
+ assert(t.r < (int64_t)vec_len(&rmap));
+ int64_t r = reg_at(rmap, t.r);
+ assert(r);
+ return reg_val(r);
+}
+
+static size_t cur_reg = 0;
+static void add_rewrite_rule(struct vec *rmap, struct val t)
+{
+ assert(t.class == TMP);
+ assert(cur_reg < 25
+ && "ran out of temp registers, time to implement proper regalloc!");
+ /* note <=, we go one 'beyond' just to make sure that 0 fits */
+ while ((int64_t)vec_len(rmap) <= t.r) {
+ int64_t zero = 0;
+ vec_append(rmap, &zero);
+ }
+
+ if (reg_at(*rmap, t.r) == 0)
+ reg_at(*rmap, t.r) = tr_map[cur_reg++];
+}
+
+void regalloc(struct fn *f)
+{
+ /* very ugly, fix once we get the proper regalloc implemented */
+ cur_reg = 0;
+ struct vec rmap = vec_create(sizeof(int64_t));
+
+ /* here we would ideally do some kind of lifetime checking, to start
+ * with we only assign to different temporary registers, we have enough
+ * of them to work for some smaller test functions */
+ /* lifetime info would probably also be useful for saving callee-save
+ * registers during calls, so that should probably also be done during
+ * register allocation? maybe? */
+ foreach_blk(bi, f->blks) {
+ struct blk *b = blk_at(f->blks, bi);
+ foreach_insn(i, b->insns) {
+ struct insn n = insn_at(b->insns, i);
+ if (n.in[0].class == TMP) {
+ n.in[0] = rewrite_tmp(rmap, n.in[0]);
+ }
+
+ if (n.in[1].class == TMP) {
+ n.in[1] = rewrite_tmp(rmap, n.in[1]);
+ }
+
+ if (n.out.class == TMP) {
+ add_rewrite_rule(&rmap, n.out);
+ n.out = rewrite_tmp(rmap, n.out);
+ }
+
+ /* write back changes */
+ insn_at(b->insns, i) = n;
+
+ /* do this here since the register allocation is the
+ * last stage before actually lowering to assembly, so
+ * no possibility of dead code elimination or stuff like
+ * that */
+ if (n.type == CALL)
+ f->has_calls = true;
+ }
+
+ if (b->cmp[0].class == TMP)
+ b->cmp[0] = rewrite_tmp(rmap, b->cmp[0]);
+
+ if (b->cmp[1].class == TMP)
+ b->cmp[1] = rewrite_tmp(rmap, b->cmp[1]);
+ }
+
+ f->max_callee_save = cur_reg;
+ vec_destroy(&rmap);
+}