aboutsummaryrefslogtreecommitdiff
path: root/src/regalloc.c
blob: 474e5a4a85c4bc946655c33c2a74ce9326a466f1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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);
}