1#include "all.h"
2
3enum {
4 Ki = -1, /* matches Kw and Kl */
5 Ka = -2, /* matches all classes */
6};
7
8static 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
99static 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
118static int64_t
119slot(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
129static void
130emitaddr(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
142static void
143emitf(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
231static void
232loadcon(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
257static void
258fixslot(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
274static void
275emitins(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
410void
411rv64_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