-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgen_x86.d
267 lines (246 loc) · 8.5 KB
/
gen_x86.d
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/// コード生成器
module gen_x86;
import std.stdio : writeln, writefln, stderr;
import std.format : format;
import core.stdc.ctype : isgraph;
import std.algorithm : among;
import gen_ir;
import regalloc;
import parser;
import sema;
import util;
public:
void generate_x86(Variable[] globals, Function[] fns)
{
writefln(".intel_syntax noprefix"); // intel記法を使う
writefln(".data");
foreach (var; globals)
{
if (var.is_extern)
continue;
writefln("%s:", var.name);
writefln(" .ascii \"%s\"", escape(cast(string) var.data));
}
writefln(".text");
size_t labelcnt;
foreach (fn; fns)
gen(fn, labelcnt);
}
private:
static immutable string[] regs_arg8 = ["dil", "sil", "dl", "cl", "r8b", "r9b"];
static immutable string[] regs_arg32 = ["edi", "esi", "edx", "ecx", "r8d", "r9d"];
static immutable string[] regs_arg64 = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
void gen(Function fn, ref size_t labelcnt)
{
writefln(".global %s", fn.name);
writefln("%s:", fn.name);
writefln(" push rbp");
writefln(" mov rbp, rsp");
// 16バイト整列は浮動小数が出てくると大事になる
writefln(" sub rsp, %d", roundup(fn.stacksize, 16));
// 終了処理の開始位置に置くラベル
string ret = format(".Lend%d", labelcnt);
labelcnt++;
// レジスタをスタックに退避
// rspはrbpから復元
enum save_regs = ["rbx", "r12", "r13", "r14", "r15"];
static foreach (reg; save_regs)
{
writefln(" push %s", reg);
}
// 最後に出力するものだけど上と対になってるので近くに置いたほうが読みやすいかなと思った
scope (success)
{
// スタックからレジスタを復元
// rspはrbpから復元
writefln("%s:", ret);
static foreach_reverse (reg; save_regs)
{
writefln(" pop %s", reg);
}
writefln(" mov rsp, rbp");
writefln(" pop rbp");
writefln(" ret");
writeln();
}
writeln();
foreach (ir; fn.irs)
{
// 計算結果はlhsのレジスタに格納
switch (ir.op)
{
case IRType.IMM:
writefln(" mov %s, %d", registers[ir.lhs], ir.rhs);
break;
case IRType.MOV:
writefln(" mov %s, %s", registers[ir.lhs], registers[ir.rhs]);
break;
case IRType.RETURN:
writefln(" mov rax, %s", registers[ir.lhs]);
writefln(" jmp %s", ret);
break;
case IRType.LOAD64:
writefln(" mov %s, [%s]", registers[ir.lhs], registers[ir.rhs]);
break;
case IRType.STORE64:
writefln(" mov [%s], %s", registers[ir.lhs], registers[ir.rhs]);
break;
case IRType.LOAD32:
writefln(" mov %s, [%s]",
registers_lower_32bits[ir.lhs], registers[ir.rhs]);
break;
case IRType.STORE32:
writefln(" mov [%s], %s", registers[ir.lhs],
registers_lower_32bits[ir.rhs]);
break;
case IRType.LOAD8:
writefln(" mov %s, [%s]",
registers_lower_8bits[ir.lhs], registers[ir.rhs]);
writefln(" movzb %s, %s", registers[ir.lhs], registers_lower_8bits[ir.lhs]);
break;
case IRType.STORE8:
writefln(" mov [%s], %s", registers[ir.lhs],
registers_lower_8bits[ir.rhs]);
break;
case IRType.ADD_IMM:
writefln(" add %s, %d", registers[ir.lhs], ir.rhs);
break;
case IRType.BPREL:
writefln(" lea %s, [rbp - %d]", registers[ir.lhs], ir.rhs);
break;
case IRType.ADD:
writefln(" add %s, %s", registers[ir.lhs], registers[ir.rhs]);
break;
case IRType.SUB:
writefln(" sub %s, %s", registers[ir.lhs], registers[ir.rhs]);
break;
case IRType.MUL:
// ここ写経元だとrhs *= lhsになってた
writefln(" mov rax, %s", registers[ir.lhs]);
writefln(" mul %s", registers[ir.rhs]);
writefln(" mov %s, rax", registers[ir.lhs]);
break;
case IRType.DIV:
writefln(" mov rax, %s", registers[ir.lhs]);
// 符号拡張
// 負数を扱うときとかにこれがないとバグる
writefln(" cqo");
// raxに商、rdxに剰余が入る
writefln(" div %s", registers[ir.rhs]);
writefln(" mov %s, rax", registers[ir.lhs]);
break;
case IRType.LABEL:
writefln(".L%d:", ir.lhs);
break;
case IRType.LABEL_ADDRESS:
// 実効アドレスを計算する
writefln(" lea %s, %s", registers[ir.lhs], ir.name);
break;
case IRType.IF:
// 右辺(この場合0)との差が0ならゼロフラグが1になる
writefln(" cmp %s, 0", registers[ir.lhs]);
// ゼロフラグが0ならジャンプ
writefln(" jne .L%d", ir.rhs);
break;
case IRType.UNLESS:
// 右辺(この場合0)との差が0ならゼロフラグが1になる
writefln(" cmp %s, 0", registers[ir.lhs]);
// ゼロフラグが1ならジャンプ
writefln(" je .L%d", ir.rhs);
break;
case IRType.JMP:
writefln(" jmp .L%d", ir.lhs);
break;
case IRType.CALL:
foreach (i, arg; ir.args)
{
writefln(" mov %s, %s", regs_arg64[i], registers[arg]);
}
// レジスタの退避
writefln(" push r10");
writefln(" push r11");
writefln(" mov rax, 0");
writefln(" call %s", ir.name);
// レジスタの復元
writefln(" pop r11");
writefln(" pop r10");
writefln(" mov %s, rax", registers[ir.lhs]);
break;
case IRType.STORE8_ARG:
// rbpからの相対アドレス
writefln(" mov [rbp-%d], %s", ir.lhs, regs_arg8[ir.rhs]);
break;
case IRType.STORE32_ARG:
writefln(" mov [rbp-%d], %s", ir.lhs, regs_arg32[ir.rhs]);
break;
case IRType.STORE64_ARG:
writefln(" mov [rbp-%d], %s", ir.lhs, regs_arg64[ir.rhs]);
break;
case IRType.LESS_THAN:
// lhs - rhsが負数になり符号フラグが立った場合1を格納
emitCmp(ir, "setl");
break;
case IRType.EQUAL:
// lhs - rhsの結果ゼロフラグが立った場合1を格納
emitCmp(ir, "sete");
break;
case IRType.NOT_EQUAL:
// lhs - rhsの結果ゼロフラグが立たなかったら1を格納
emitCmp(ir, "setne");
break;
case IRType.NOP:
break;
default:
assert(0, "Unknown operator");
}
}
}
string genLabel(size_t labelcnt)
{
return format(".L%d", labelcnt);
}
string escape(string s)
{
// token.dのescapedとは中身が違うので注意
static immutable char[256] escaped = () {
import std.format : format;
char[256] a;
a[] = 0;
static foreach (char c; "bfnrt\\\'\"")
{
a[mixin(format("'\\%c'", c))] = c;
}
return a;
}();
string result;
foreach (c; s)
{
char esc = escaped[c];
if (esc != 0)
{
result ~= "\\";
result ~= esc;
}
else if (isgraph(c) || c == ' ')
{
result ~= c;
}
else
{
// 3桁8進数
result ~= format("\\%03o", c);
}
}
return result;
}
void emitCmp(IR ir, string insn)
{
// 比較の結果さまざまなフラグが立ったり立たなかったりする
writefln(" cmp %s, %s", registers[ir.lhs], registers[ir.rhs]);
// 結果は1バイトの値なのでレジスタも8ビットのものを使う
writefln(" %s %s", insn, registers_lower_8bits[ir.lhs]);
// 上位ビットは変化しないの���適切にゼロ拡張してやる必要がある。
// movzbは8ビットの値を64ビットにゼロ拡張して格納する。
// 結果的に、下のコードはlhsレジスタの上位ビットをただゼロ埋めする
writefln(" movzb %s, %s", registers[ir.lhs], registers_lower_8bits[ir.lhs]);
}