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
|
#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(), 0);
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(), 0);
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(), 0);
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(), 0);
b->cmp[1] = t;
}
}
}
|