1 | #include "all.h" |
2 | |
3 | enum { |
4 | Ki = -1, /* matches Kw and Kl */ |
5 | Ka = -2, /* matches all classes */ |
6 | }; |
7 | |
8 | static struct { |
9 | short op; |
10 | short cls; |
11 | char *asm; |
12 | } omap[] = { |
13 | { Oadd, Ki, "add%k %=, %0, %1" }, |
14 | { Oadd, Ka, "fadd.%k %=, %0, %1" }, |
15 | { Osub, Ki, "sub%k %=, %0, %1" }, |
16 | { Osub, Ka, "fsub.%k %=, %0, %1" }, |
17 | { Oneg, Ki, "neg%k %=, %0" }, |
18 | { Oneg, Ka, "fneg.%k %=, %0" }, |
19 | { Odiv, Ki, "div%k %=, %0, %1" }, |
20 | { Odiv, Ka, "fdiv.%k %=, %0, %1" }, |
21 | { Orem, Ki, "rem%k %=, %0, %1" }, |
22 | { Orem, Kl, "rem %=, %0, %1" }, |
23 | { Oudiv, Ki, "divu%k %=, %0, %1" }, |
24 | { Ourem, Ki, "remu%k %=, %0, %1" }, |
25 | { Omul, Ki, "mul%k %=, %0, %1" }, |
26 | { Omul, Ka, "fmul.%k %=, %0, %1" }, |
27 | { Oand, Ki, "and %=, %0, %1" }, |
28 | { Oor, Ki, "or %=, %0, %1" }, |
29 | { Oxor, Ki, "xor %=, %0, %1" }, |
30 | { Osar, Ki, "sra%k %=, %0, %1" }, |
31 | { Oshr, Ki, "srl%k %=, %0, %1" }, |
32 | { Oshl, Ki, "sll%k %=, %0, %1" }, |
33 | { Ocsltl, Ki, "slt %=, %0, %1" }, |
34 | { Ocultl, Ki, "sltu %=, %0, %1" }, |
35 | { Oceqs, Ki, "feq.s %=, %0, %1" }, |
36 | { Ocges, Ki, "fge.s %=, %0, %1" }, |
37 | { Ocgts, Ki, "fgt.s %=, %0, %1" }, |
38 | { Ocles, Ki, "fle.s %=, %0, %1" }, |
39 | { Oclts, Ki, "flt.s %=, %0, %1" }, |
40 | { Oceqd, Ki, "feq.d %=, %0, %1" }, |
41 | { Ocged, Ki, "fge.d %=, %0, %1" }, |
42 | { Ocgtd, Ki, "fgt.d %=, %0, %1" }, |
43 | { Ocled, Ki, "fle.d %=, %0, %1" }, |
44 | { Ocltd, Ki, "flt.d %=, %0, %1" }, |
45 | { Ostoreb, Kw, "sb %0, %M1" }, |
46 | { Ostoreh, Kw, "sh %0, %M1" }, |
47 | { Ostorew, Kw, "sw %0, %M1" }, |
48 | { Ostorel, Ki, "sd %0, %M1" }, |
49 | { Ostores, Kw, "fsw %0, %M1" }, |
50 | { Ostored, Kw, "fsd %0, %M1" }, |
51 | { Oloadsb, Ki, "lb %=, %M0" }, |
52 | { Oloadub, Ki, "lbu %=, %M0" }, |
53 | { Oloadsh, Ki, "lh %=, %M0" }, |
54 | { Oloaduh, Ki, "lhu %=, %M0" }, |
55 | { Oloadsw, Ki, "lw %=, %M0" }, |
56 | /* riscv64 always sign-extends 32-bit |
57 | * values stored in 64-bit registers |
58 | */ |
59 | { Oloaduw, Kw, "lw %=, %M0" }, |
60 | { Oloaduw, Kl, "lwu %=, %M0" }, |
61 | { Oload, Kw, "lw %=, %M0" }, |
62 | { Oload, Kl, "ld %=, %M0" }, |
63 | { Oload, Ks, "flw %=, %M0" }, |
64 | { Oload, Kd, "fld %=, %M0" }, |
65 | { Oextsb, Ki, "sext.b %=, %0" }, |
66 | { Oextub, Ki, "zext.b %=, %0" }, |
67 | { Oextsh, Ki, "sext.h %=, %0" }, |
68 | { Oextuh, Ki, "zext.h %=, %0" }, |
69 | { Oextsw, Kl, "sext.w %=, %0" }, |
70 | { Oextuw, Kl, "zext.w %=, %0" }, |
71 | { Otruncd, Ks, "fcvt.s.d %=, %0" }, |
72 | { Oexts, Kd, "fcvt.d.s %=, %0" }, |
73 | { Ostosi, Kw, "fcvt.w.s %=, %0, rtz" }, |
74 | { Ostosi, Kl, "fcvt.l.s %=, %0, rtz" }, |
75 | { Ostoui, Kw, "fcvt.wu.s %=, %0, rtz" }, |
76 | { Ostoui, Kl, "fcvt.lu.s %=, %0, rtz" }, |
77 | { Odtosi, Kw, "fcvt.w.d %=, %0, rtz" }, |
78 | { Odtosi, Kl, "fcvt.l.d %=, %0, rtz" }, |
79 | { Odtoui, Kw, "fcvt.wu.d %=, %0, rtz" }, |
80 | { Odtoui, Kl, "fcvt.lu.d %=, %0, rtz" }, |
81 | { Oswtof, Ka, "fcvt.%k.w %=, %0" }, |
82 | { Ouwtof, Ka, "fcvt.%k.wu %=, %0" }, |
83 | { Osltof, Ka, "fcvt.%k.l %=, %0" }, |
84 | { Oultof, Ka, "fcvt.%k.lu %=, %0" }, |
85 | { Ocast, Kw, "fmv.x.w %=, %0" }, |
86 | { Ocast, Kl, "fmv.x.d %=, %0" }, |
87 | { Ocast, Ks, "fmv.w.x %=, %0" }, |
88 | { Ocast, Kd, "fmv.d.x %=, %0" }, |
89 | { Ocopy, Ki, "mv %=, %0" }, |
90 | { Ocopy, Ka, "fmv.%k %=, %0" }, |
91 | { Oswap, Ki, "mv %?, %0\n\tmv %0, %1\n\tmv %1, %?" }, |
92 | { Oswap, Ka, "fmv.%k %?, %0\n\tfmv.%k %0, %1\n\tfmv.%k %1, %?" }, |
93 | { Oreqz, Ki, "seqz %=, %0" }, |
94 | { Ornez, Ki, "snez %=, %0" }, |
95 | { Ocall, Kw, "jalr %0" }, |
96 | { NOp, 0, 0 } |
97 | }; |
98 | |
99 | static char *rname[] = { |
100 | [FP] = "fp" , |
101 | [SP] = "sp" , |
102 | [GP] = "gp" , |
103 | [TP] = "tp" , |
104 | [RA] = "ra" , |
105 | [T0] = "t0" , "t1" , "t2" , "t3" , "t4" , "t5" , |
106 | [A0] = "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , "a6" , "a7" , |
107 | [S1] = "s1" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" , "s8" , |
108 | "s9" , "s10" , "s11" , |
109 | [FT0] = "ft0" , "ft1" , "ft2" , "ft3" , "ft4" , "ft5" , "ft6" , "ft7" , |
110 | "ft8" , "ft9" , "ft10" , |
111 | [FA0] = "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , "fa6" , "fa7" , |
112 | [FS0] = "fs0" , "fs1" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , |
113 | "fs8" , "fs9" , "fs10" , "fs11" , |
114 | [T6] = "t6" , |
115 | [FT11] = "ft11" , |
116 | }; |
117 | |
118 | static int64_t |
119 | slot(int s, Fn *fn) |
120 | { |
121 | s = ((int32_t)s << 3) >> 3; |
122 | assert(s <= fn->slot); |
123 | if (s < 0) |
124 | return 8 * -s; |
125 | else |
126 | return -4 * (fn->slot - s); |
127 | } |
128 | |
129 | static void |
130 | emitaddr(Con *c, FILE *f) |
131 | { |
132 | char off[32], *p; |
133 | |
134 | if (c->bits.i) |
135 | sprintf(off, "+%" PRIi64, c->bits.i); |
136 | else |
137 | off[0] = 0; |
138 | p = c->local ? ".L" : "" ; |
139 | fprintf(f, "%s%s%s" , p, str(c->label), off); |
140 | } |
141 | |
142 | static void |
143 | emitf(char *s, Ins *i, Fn *fn, FILE *f) |
144 | { |
145 | static char clschr[] = {'w', 'l', 's', 'd'}; |
146 | Ref r; |
147 | int k, c; |
148 | Con *pc; |
149 | int64_t offset; |
150 | |
151 | fputc('\t', f); |
152 | for (;;) { |
153 | k = i->cls; |
154 | while ((c = *s++) != '%') |
155 | if (!c) { |
156 | fputc('\n', f); |
157 | return; |
158 | } else |
159 | fputc(c, f); |
160 | switch ((c = *s++)) { |
161 | default: |
162 | die("invalid escape" ); |
163 | case '?': |
164 | if (KBASE(k) == 0) |
165 | fputs("t6" , f); |
166 | else |
167 | fputs("ft11" , f); |
168 | break; |
169 | case 'k': |
170 | if (i->cls != Kl) |
171 | fputc(clschr[i->cls], f); |
172 | break; |
173 | case '=': |
174 | case '0': |
175 | r = c == '=' ? i->to : i->arg[0]; |
176 | assert(isreg(r)); |
177 | fputs(rname[r.val], f); |
178 | break; |
179 | case '1': |
180 | r = i->arg[1]; |
181 | switch (rtype(r)) { |
182 | default: |
183 | die("invalid second argument" ); |
184 | case RTmp: |
185 | assert(isreg(r)); |
186 | fputs(rname[r.val], f); |
187 | break; |
188 | case RCon: |
189 | pc = &fn->con[r.val]; |
190 | assert(pc->type == CBits); |
191 | assert(pc->bits.i >= -2048 && pc->bits.i < 2048); |
192 | fprintf(f, "%d" , (int)pc->bits.i); |
193 | break; |
194 | } |
195 | break; |
196 | case 'M': |
197 | c = *s++; |
198 | assert(c == '0' || c == '1'); |
199 | r = i->arg[c - '0']; |
200 | switch (rtype(r)) { |
201 | default: |
202 | die("invalid address argument" ); |
203 | case RTmp: |
204 | fprintf(f, "0(%s)" , rname[r.val]); |
205 | break; |
206 | case RCon: |
207 | pc = &fn->con[r.val]; |
208 | assert(pc->type == CAddr); |
209 | emitaddr(pc, f); |
210 | if (isstore(i->op) |
211 | || (isload(i->op) && KBASE(i->cls) == 1)) { |
212 | /* store (and float load) |
213 | * pseudo-instructions need a |
214 | * temporary register in which to |
215 | * load the address |
216 | */ |
217 | fprintf(f, ", t6" ); |
218 | } |
219 | break; |
220 | case RSlot: |
221 | offset = slot(r.val, fn); |
222 | assert(offset >= -2048 && offset <= 2047); |
223 | fprintf(f, "%d(fp)" , (int)offset); |
224 | break; |
225 | } |
226 | break; |
227 | } |
228 | } |
229 | } |
230 | |
231 | static void |
232 | loadcon(Con *c, int r, int k, FILE *f) |
233 | { |
234 | char *rn; |
235 | int64_t n; |
236 | int w; |
237 | |
238 | w = KWIDE(k); |
239 | rn = rname[r]; |
240 | switch (c->type) { |
241 | case CAddr: |
242 | fprintf(f, "\tla %s, " , rn); |
243 | emitaddr(c, f); |
244 | fputc('\n', f); |
245 | break; |
246 | case CBits: |
247 | n = c->bits.i; |
248 | if (!w) |
249 | n = (int32_t)n; |
250 | fprintf(f, "\tli %s, %" PRIu64"\n" , rn, n); |
251 | break; |
252 | default: |
253 | die("invalid constant" ); |
254 | } |
255 | } |
256 | |
257 | static void |
258 | fixslot(Ref *pr, Fn *fn, FILE *f) |
259 | { |
260 | Ref r; |
261 | int64_t s; |
262 | |
263 | r = *pr; |
264 | if (rtype(r) == RSlot) { |
265 | s = slot(r.val, fn); |
266 | if (s < -2048 || s > 2047) { |
267 | fprintf(f, "\tli t6, %" PRId64"\n" , s); |
268 | fprintf(f, "\tadd t6, fp, t6\n" ); |
269 | *pr = TMP(T6); |
270 | } |
271 | } |
272 | } |
273 | |
274 | static void |
275 | emitins(Ins *i, Fn *fn, FILE *f) |
276 | { |
277 | int o; |
278 | char *rn; |
279 | int64_t s; |
280 | Con *con; |
281 | |
282 | switch (i->op) { |
283 | default: |
284 | if (isload(i->op)) |
285 | fixslot(&i->arg[0], fn, f); |
286 | else if (isstore(i->op)) |
287 | fixslot(&i->arg[1], fn, f); |
288 | Table: |
289 | /* most instructions are just pulled out of |
290 | * the table omap[], some special cases are |
291 | * detailed below */ |
292 | for (o=0;; o++) { |
293 | /* this linear search should really be a binary |
294 | * search */ |
295 | if (omap[o].op == NOp) |
296 | die("no match for %s(%c)" , |
297 | optab[i->op].name, "wlsd" [i->cls]); |
298 | if (omap[o].op == i->op) |
299 | if (omap[o].cls == i->cls || omap[o].cls == Ka |
300 | || (omap[o].cls == Ki && KBASE(i->cls) == 0)) |
301 | break; |
302 | } |
303 | emitf(omap[o].asm, i, fn, f); |
304 | break; |
305 | case Ocopy: |
306 | if (req(i->to, i->arg[0])) |
307 | break; |
308 | if (rtype(i->to) == RSlot) { |
309 | switch (rtype(i->arg[0])) { |
310 | case RSlot: |
311 | case RCon: |
312 | die("unimplemented" ); |
313 | break; |
314 | default: |
315 | assert(isreg(i->arg[0])); |
316 | i->arg[1] = i->to; |
317 | i->to = R; |
318 | switch (i->cls) { |
319 | case Kw: i->op = Ostorew; break; |
320 | case Kl: i->op = Ostorel; break; |
321 | case Ks: i->op = Ostores; break; |
322 | case Kd: i->op = Ostored; break; |
323 | } |
324 | fixslot(&i->arg[1], fn, f); |
325 | goto Table; |
326 | } |
327 | break; |
328 | } |
329 | assert(isreg(i->to)); |
330 | switch (rtype(i->arg[0])) { |
331 | case RCon: |
332 | loadcon(&fn->con[i->arg[0].val], i->to.val, i->cls, f); |
333 | break; |
334 | case RSlot: |
335 | i->op = Oload; |
336 | fixslot(&i->arg[0], fn, f); |
337 | goto Table; |
338 | default: |
339 | assert(isreg(i->arg[0])); |
340 | goto Table; |
341 | } |
342 | break; |
343 | case Onop: |
344 | break; |
345 | case Oaddr: |
346 | assert(rtype(i->arg[0]) == RSlot); |
347 | rn = rname[i->to.val]; |
348 | s = slot(i->arg[0].val, fn); |
349 | if (-s < 2048) { |
350 | fprintf(f, "\tadd %s, fp, %" PRId64"\n" , rn, s); |
351 | } else { |
352 | fprintf(f, |
353 | "\tli %s, %" PRId64"\n" |
354 | "\tadd %s, fp, %s\n" , |
355 | rn, s, rn, rn |
356 | ); |
357 | } |
358 | break; |
359 | case Ocall: |
360 | switch (rtype(i->arg[0])) { |
361 | case RCon: |
362 | con = &fn->con[i->arg[0].val]; |
363 | if (con->type != CAddr || con->bits.i) |
364 | goto invalid; |
365 | fprintf(f, "\tcall %s\n" , str(con->label)); |
366 | break; |
367 | case RTmp: |
368 | emitf("jalr %0" , i, fn, f); |
369 | break; |
370 | default: |
371 | invalid: |
372 | die("invalid call argument" ); |
373 | } |
374 | break; |
375 | case Osalloc: |
376 | emitf("sub sp, sp, %0" , i, fn, f); |
377 | if (!req(i->to, R)) |
378 | emitf("mv %=, sp" , i, fn, f); |
379 | break; |
380 | } |
381 | } |
382 | |
383 | /* |
384 | |
385 | Stack-frame layout: |
386 | |
387 | +=============+ |
388 | | varargs | |
389 | | save area | |
390 | +-------------+ |
391 | | saved ra | |
392 | | saved fp | |
393 | +-------------+ <- fp |
394 | | ... | |
395 | | spill slots | |
396 | | ... | |
397 | +-------------+ |
398 | | ... | |
399 | | locals | |
400 | | ... | |
401 | +-------------+ |
402 | | padding | |
403 | +-------------+ |
404 | | callee-save | |
405 | | registers | |
406 | +=============+ |
407 | |
408 | */ |
409 | |
410 | void |
411 | rv64_emitfn(Fn *fn, FILE *f) |
412 | { |
413 | static int id0; |
414 | int lbl, neg, off, frame, *pr, r; |
415 | Blk *b, *s; |
416 | Ins *i; |
417 | |
418 | gasemitlnk(fn->name, &fn->lnk, ".text" , f); |
419 | |
420 | if (fn->vararg) { |
421 | /* TODO: only need space for registers |
422 | * unused by named arguments |
423 | */ |
424 | fprintf(f, "\tadd sp, sp, -64\n" ); |
425 | for (r=A0; r<=A7; r++) |
426 | fprintf(f, |
427 | "\tsd %s, %d(sp)\n" , |
428 | rname[r], 8 * (r - A0) |
429 | ); |
430 | } |
431 | fprintf(f, "\tsd fp, -16(sp)\n" ); |
432 | fprintf(f, "\tsd ra, -8(sp)\n" ); |
433 | fprintf(f, "\tadd fp, sp, -16\n" ); |
434 | |
435 | frame = (16 + 4 * fn->slot + 15) & ~15; |
436 | for (pr=rv64_rclob; *pr>=0; pr++) { |
437 | if (fn->reg & BIT(*pr)) |
438 | frame += 8; |
439 | } |
440 | frame = (frame + 15) & ~15; |
441 | |
442 | if (frame <= 2048) |
443 | fprintf(f, |
444 | "\tadd sp, sp, -%d\n" , |
445 | frame |
446 | ); |
447 | else |
448 | fprintf(f, |
449 | "\tli t6, %d\n" |
450 | "\tsub sp, sp, t6\n" , |
451 | frame |
452 | ); |
453 | for (pr=rv64_rclob, off=0; *pr>=0; pr++) { |
454 | if (fn->reg & BIT(*pr)) { |
455 | fprintf(f, |
456 | "\t%s %s, %d(sp)\n" , |
457 | *pr < FT0 ? "sd" : "fsd" , |
458 | rname[*pr], off |
459 | ); |
460 | off += 8; |
461 | } |
462 | } |
463 | |
464 | for (lbl=0, b=fn->start; b; b=b->link) { |
465 | if (lbl || b->npred > 1) |
466 | fprintf(f, ".L%d:\n" , id0+b->id); |
467 | for (i=b->ins; i!=&b->ins[b->nins]; i++) |
468 | emitins(i, fn, f); |
469 | lbl = 1; |
470 | switch (b->jmp.type) { |
471 | case Jret0: |
472 | if (fn->dynalloc) { |
473 | if (frame - 16 <= 2048) |
474 | fprintf(f, |
475 | "\tadd sp, fp, -%d\n" , |
476 | frame - 16 |
477 | ); |
478 | else |
479 | fprintf(f, |
480 | "\tli t6, %d\n" |
481 | "\tsub sp, fp, t6\n" , |
482 | frame - 16 |
483 | ); |
484 | } |
485 | for (pr=rv64_rclob, off=0; *pr>=0; pr++) { |
486 | if (fn->reg & BIT(*pr)) { |
487 | fprintf(f, |
488 | "\t%s %s, %d(sp)\n" , |
489 | *pr < FT0 ? "ld" : "fld" , |
490 | rname[*pr], off |
491 | ); |
492 | off += 8; |
493 | } |
494 | } |
495 | fprintf(f, |
496 | "\tadd sp, fp, %d\n" |
497 | "\tld ra, 8(fp)\n" |
498 | "\tld fp, 0(fp)\n" |
499 | "\tret\n" , |
500 | 16 + fn->vararg * 64 |
501 | ); |
502 | break; |
503 | case Jjmp: |
504 | Jmp: |
505 | if (b->s1 != b->link) |
506 | fprintf(f, "\tj .L%d\n" , id0+b->s1->id); |
507 | else |
508 | lbl = 0; |
509 | break; |
510 | case Jjnz: |
511 | neg = 0; |
512 | if (b->link == b->s2) { |
513 | s = b->s1; |
514 | b->s1 = b->s2; |
515 | b->s2 = s; |
516 | neg = 1; |
517 | } |
518 | assert(isreg(b->jmp.arg)); |
519 | fprintf(f, |
520 | "\tb%sz %s, .L%d\n" , |
521 | neg ? "ne" : "eq" , |
522 | rname[b->jmp.arg.val], |
523 | id0+b->s2->id |
524 | ); |
525 | goto Jmp; |
526 | } |
527 | } |
528 | id0 += fn->nblk; |
529 | } |
530 | |