-
Notifications
You must be signed in to change notification settings - Fork 155
/
regalloc.c
203 lines (165 loc) · 4.57 KB
/
regalloc.c
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Linear scan register allocator.
//
// Before this pass, it is assumed that we have infinite number of
// registers. This pass maps them to finite number of registers.
// Here is the algorithm:
//
// First, we find the definition and the last use for each register.
// A register is considered "live" in the range. At the definition of
// some register R, if all physical registers are already allocated,
// one of them (including R itself) needs to be spilled to the stack.
// As long as one register is spilled, the algorithm is logically
// correct. As a heuristic, we spill a register whose last use is
// furthest.
//
// We then insert load and store instructions for spilled registesr.
// The last register (num_regs-1'th register) is reserved for that
// purpose.
#include "9cc.h"
// Rewrite `A = B op C` to `A = B; A = A op C`.
static void three_to_two(BB *bb) {
Vector *v = new_vec();
for (int i = 0; i < bb->ir->len; i++) {
IR *ir = bb->ir->data[i];
if (!ir->r0 || !ir->r1) {
vec_push(v, ir);
continue;
}
assert(ir->r0 != ir->r1);
IR *ir2 = calloc(1, sizeof(IR));
ir2->op = IR_MOV;
ir2->r0 = ir->r0;
ir2->r2 = ir->r1;
vec_push(v, ir2);
ir->r1 = ir->r0;
vec_push(v, ir);
}
bb->ir = v;
}
static void set_last_use(Reg *r, int ic) {
if (r && r->last_use < ic)
r->last_use = ic;
}
static Vector *collect_regs(Function *fn) {
Vector *v = new_vec();
int ic = 1; // instruction counter
for (int i = 0; i < fn->bbs->len; i++) {
BB *bb = fn->bbs->data[i];
if (bb->param) {
bb->param->def = ic;
vec_push(v, bb->param);
}
for (int i = 0; i < bb->ir->len; i++, ic++) {
IR *ir = bb->ir->data[i];
if (ir->r0 && !ir->r0->def) {
ir->r0->def = ic;
vec_push(v, ir->r0);
}
set_last_use(ir->r1, ic);
set_last_use(ir->r2, ic);
set_last_use(ir->bbarg, ic);
if (ir->op == IR_CALL)
for (int i = 0; i < ir->nargs; i++)
set_last_use(ir->args[i], ic);
}
for (int i = 0; i < bb->out_regs->len; i++) {
Reg *r = bb->out_regs->data[i];
set_last_use(r, ic);
}
}
return v;
}
static int choose_to_spill(Reg **used) {
int k = 0;
for (int i = 1; i < num_regs; i++)
if (used[k]->last_use < used[i]->last_use)
k = i;
return k;
}
// Allocate registers.
static void scan(Vector *regs) {
Reg **used = calloc(num_regs, sizeof(Reg *));
for (int i = 0; i < regs->len; i++) {
Reg *r = regs->data[i];
// Find an unused slot.
bool found = false;
for (int i = 0; i < num_regs - 1; i++) {
if (used[i] && r->def < used[i]->last_use)
continue;
r->rn = i;
used[i] = r;
found = true;
break;
}
if (found)
continue;
// Choose a register to spill and mark it as "spilled".
used[num_regs - 1] = r;
int k = choose_to_spill(used);
r->rn = k;
used[k]->rn = num_regs - 1;
used[k]->spill = true;
used[k] = r;
}
}
static void spill_store(Vector *v, IR *ir) {
Reg *r = ir->r0;
if (!r || !r->spill)
return;
IR *ir2 = calloc(1, sizeof(IR));
ir2->op = IR_STORE_SPILL;
ir2->r1 = r;
ir2->var = r->var;
vec_push(v, ir2);
}
static void spill_load(Vector *v, IR *ir, Reg *r) {
if (!r || !r->spill)
return;
IR *ir2 = calloc(1, sizeof(IR));
ir2->op = IR_LOAD_SPILL;
ir2->r0 = r;
ir2->var = r->var;
vec_push(v, ir2);
}
static void emit_spill_code(BB *bb) {
Vector *v = new_vec();
for (int i = 0; i < bb->ir->len; i++) {
IR *ir = bb->ir->data[i];
spill_load(v, ir, ir->r1);
spill_load(v, ir, ir->r2);
spill_load(v, ir, ir->bbarg);
vec_push(v, ir);
spill_store(v, ir);
}
bb->ir = v;
}
void alloc_regs(Program *prog) {
for (int i = 0; i < prog->funcs->len; i++) {
Function *fn = prog->funcs->data[i];
// Convert SSA to x86-ish two-address form.
for (int i = 0; i < fn->bbs->len; i++) {
BB *bb = fn->bbs->data[i];
three_to_two(bb);
}
// Allocate registers and decide which registers to spill.
Vector *regs = collect_regs(fn);
scan(regs);
// Reserve a stack area for spilled registers.
for (int i = 0; i < regs->len; i++) {
Reg *r = regs->data[i];
if (!r->spill)
continue;
Var *var = calloc(1, sizeof(Var));
var->ty = ptr_to(int_ty());
var->is_local = true;
var->name = "spill";
r->var = var;
vec_push(fn->lvars, var);
}
// Convert accesses to spilled registers to loads and stores.
for (int i = 0; i < fn->bbs->len; i++) {
BB *bb = fn->bbs->data[i];
emit_spill_code(bb);
}
}
}