stored bytes in Alias information
[qbe.git] / rv64 / emit.c
blobbb53c838e675e750a812145a83e05e7c8699be87
1 #include "all.h"
3 enum {
4 Ki = -1, /* matches Kw and Kl */
5 Ka = -2, /* matches all classes */
6 };
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
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 }
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",
118 static int64_t
119 slot(int s, Fn *fn)
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);
129 static void
130 emitaddr(Con *c, FILE *f)
132 assert(c->reloc == RelDef);
133 fputs(str(c->label), f);
134 if (c->bits.i)
135 fprintf(f, "+%"PRIi64, c->bits.i);
138 static void
139 emitf(char *s, Ins *i, Fn *fn, FILE *f)
141 static char clschr[] = {'w', 'l', 's', 'd'};
142 Ref r;
143 int k, c;
144 Con *pc;
145 int64_t offset;
147 fputc('\t', f);
148 for (;;) {
149 k = i->cls;
150 while ((c = *s++) != '%')
151 if (!c) {
152 fputc('\n', f);
153 return;
154 } else
155 fputc(c, f);
156 switch ((c = *s++)) {
157 default:
158 die("invalid escape");
159 case '?':
160 if (KBASE(k) == 0)
161 fputs("t6", f);
162 else
163 fputs("ft11", f);
164 break;
165 case 'k':
166 if (i->cls != Kl)
167 fputc(clschr[i->cls], f);
168 break;
169 case '=':
170 case '0':
171 r = c == '=' ? i->to : i->arg[0];
172 assert(isreg(r));
173 fputs(rname[r.val], f);
174 break;
175 case '1':
176 r = i->arg[1];
177 switch (rtype(r)) {
178 default:
179 die("invalid second argument");
180 case RTmp:
181 assert(isreg(r));
182 fputs(rname[r.val], f);
183 break;
184 case RCon:
185 pc = &fn->con[r.val];
186 assert(pc->type == CBits);
187 assert(pc->bits.i >= -2048 && pc->bits.i < 2048);
188 fprintf(f, "%d", (int)pc->bits.i);
189 break;
191 break;
192 case 'M':
193 c = *s++;
194 assert(c == '0' || c == '1');
195 r = i->arg[c - '0'];
196 switch (rtype(r)) {
197 default:
198 die("invalid address argument");
199 case RTmp:
200 fprintf(f, "0(%s)", rname[r.val]);
201 break;
202 case RCon:
203 pc = &fn->con[r.val];
204 assert(pc->type == CAddr);
205 emitaddr(pc, f);
206 if (isstore(i->op)
207 || (isload(i->op) && KBASE(i->cls) == 1)) {
208 /* store (and float load)
209 * pseudo-instructions need a
210 * temporary register in which to
211 * load the address
213 fprintf(f, ", t6");
215 break;
216 case RSlot:
217 offset = slot(r.val, fn);
218 assert(offset >= -2048 && offset <= 2047);
219 fprintf(f, "%d(fp)", (int)offset);
220 break;
222 break;
227 static void
228 loadaddr(Con *c, char *rn, FILE *f)
230 char off[32];
232 if (c->reloc == RelThr) {
233 if (c->bits.i)
234 sprintf(off, "+%"PRIi64, c->bits.i);
235 else
236 off[0] = 0;
237 fprintf(f, "\tlui %s, %%tprel_hi(%s)%s\n",
238 rn, str(c->label), off);
239 fprintf(f, "\tadd %s, %s, tp, %%tprel_add(%s)%s\n",
240 rn, rn, str(c->label), off);
241 fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n",
242 rn, rn, str(c->label), off);
243 } else {
244 fprintf(f, "\tla %s, ", rn);
245 emitaddr(c, f);
246 fputc('\n', f);
250 static void
251 loadcon(Con *c, int r, int k, FILE *f)
253 char *rn;
254 int64_t n;
256 rn = rname[r];
257 switch (c->type) {
258 case CAddr:
259 loadaddr(c, rn, f);
260 break;
261 case CBits:
262 n = c->bits.i;
263 if (!KWIDE(k))
264 n = (int32_t)n;
265 fprintf(f, "\tli %s, %"PRIi64"\n", rn, n);
266 break;
267 default:
268 die("invalid constant");
272 static void
273 fixmem(Ref *pr, Fn *fn, FILE *f)
275 Ref r;
276 int64_t s;
277 Con *c;
279 r = *pr;
280 if (rtype(r) == RCon) {
281 c = &fn->con[r.val];
282 if (c->type == CAddr && c->reloc == RelThr) {
283 loadcon(c, T6, Kl, f);
284 *pr = TMP(T6);
287 if (rtype(r) == RSlot) {
288 s = slot(r.val, fn);
289 if (s < -2048 || s > 2047) {
290 fprintf(f, "\tli t6, %"PRId64"\n", s);
291 fprintf(f, "\tadd t6, fp, t6\n");
292 *pr = TMP(T6);
297 static void
298 emitins(Ins *i, Fn *fn, FILE *f)
300 int o;
301 char *rn;
302 int64_t s;
303 Con *con;
305 switch (i->op) {
306 default:
307 if (isload(i->op))
308 fixmem(&i->arg[0], fn, f);
309 else if (isstore(i->op))
310 fixmem(&i->arg[1], fn, f);
311 Table:
312 /* most instructions are just pulled out of
313 * the table omap[], some special cases are
314 * detailed below */
315 for (o=0;; o++) {
316 /* this linear search should really be a binary
317 * search */
318 if (omap[o].op == NOp)
319 die("no match for %s(%c)",
320 optab[i->op].name, "wlsd"[i->cls]);
321 if (omap[o].op == i->op)
322 if (omap[o].cls == i->cls || omap[o].cls == Ka
323 || (omap[o].cls == Ki && KBASE(i->cls) == 0))
324 break;
326 emitf(omap[o].asm, i, fn, f);
327 break;
328 case Ocopy:
329 if (req(i->to, i->arg[0]))
330 break;
331 if (rtype(i->to) == RSlot) {
332 switch (rtype(i->arg[0])) {
333 case RSlot:
334 case RCon:
335 die("unimplemented");
336 break;
337 default:
338 assert(isreg(i->arg[0]));
339 i->arg[1] = i->to;
340 i->to = R;
341 switch (i->cls) {
342 case Kw: i->op = Ostorew; break;
343 case Kl: i->op = Ostorel; break;
344 case Ks: i->op = Ostores; break;
345 case Kd: i->op = Ostored; break;
347 fixmem(&i->arg[1], fn, f);
348 goto Table;
350 break;
352 assert(isreg(i->to));
353 switch (rtype(i->arg[0])) {
354 case RCon:
355 loadcon(&fn->con[i->arg[0].val], i->to.val, i->cls, f);
356 break;
357 case RSlot:
358 i->op = Oload;
359 fixmem(&i->arg[0], fn, f);
360 goto Table;
361 default:
362 assert(isreg(i->arg[0]));
363 goto Table;
365 break;
366 case Onop:
367 break;
368 case Oaddr:
369 assert(rtype(i->arg[0]) == RSlot);
370 rn = rname[i->to.val];
371 s = slot(i->arg[0].val, fn);
372 if (-s < 2048) {
373 fprintf(f, "\tadd %s, fp, %"PRId64"\n", rn, s);
374 } else {
375 fprintf(f,
376 "\tli %s, %"PRId64"\n"
377 "\tadd %s, fp, %s\n",
378 rn, s, rn, rn
381 break;
382 case Ocall:
383 switch (rtype(i->arg[0])) {
384 case RCon:
385 con = &fn->con[i->arg[0].val];
386 if (con->type != CAddr || con->bits.i)
387 goto Invalid;
388 fprintf(f, "\tcall %s\n", str(con->label));
389 break;
390 case RTmp:
391 emitf("jalr %0", i, fn, f);
392 break;
393 default:
394 Invalid:
395 die("invalid call argument");
397 break;
398 case Osalloc:
399 emitf("sub sp, sp, %0", i, fn, f);
400 if (!req(i->to, R))
401 emitf("mv %=, sp", i, fn, f);
402 break;
408 Stack-frame layout:
410 +=============+
411 | varargs |
412 | save area |
413 +-------------+
414 | saved ra |
415 | saved fp |
416 +-------------+ <- fp
417 | ... |
418 | spill slots |
419 | ... |
420 +-------------+
421 | ... |
422 | locals |
423 | ... |
424 +-------------+
425 | padding |
426 +-------------+
427 | callee-save |
428 | registers |
429 +=============+
433 void
434 rv64_emitfn(Fn *fn, FILE *f)
436 static int id0;
437 int lbl, neg, off, frame, *pr, r;
438 Blk *b, *s;
439 Ins *i;
441 emitfnlnk(fn->name, &fn->lnk, f);
443 if (fn->vararg) {
444 /* TODO: only need space for registers
445 * unused by named arguments
447 fprintf(f, "\tadd sp, sp, -64\n");
448 for (r=A0; r<=A7; r++)
449 fprintf(f,
450 "\tsd %s, %d(sp)\n",
451 rname[r], 8 * (r - A0)
454 fprintf(f, "\tsd fp, -16(sp)\n");
455 fprintf(f, "\tsd ra, -8(sp)\n");
456 fprintf(f, "\tadd fp, sp, -16\n");
458 frame = (16 + 4 * fn->slot + 15) & ~15;
459 for (pr=rv64_rclob; *pr>=0; pr++) {
460 if (fn->reg & BIT(*pr))
461 frame += 8;
463 frame = (frame + 15) & ~15;
465 if (frame <= 2048)
466 fprintf(f,
467 "\tadd sp, sp, -%d\n",
468 frame
470 else
471 fprintf(f,
472 "\tli t6, %d\n"
473 "\tsub sp, sp, t6\n",
474 frame
476 for (pr=rv64_rclob, off=0; *pr>=0; pr++) {
477 if (fn->reg & BIT(*pr)) {
478 fprintf(f,
479 "\t%s %s, %d(sp)\n",
480 *pr < FT0 ? "sd" : "fsd",
481 rname[*pr], off
483 off += 8;
487 for (lbl=0, b=fn->start; b; b=b->link) {
488 if (lbl || b->npred > 1)
489 fprintf(f, ".L%d:\n", id0+b->id);
490 for (i=b->ins; i!=&b->ins[b->nins]; i++)
491 emitins(i, fn, f);
492 lbl = 1;
493 switch (b->jmp.type) {
494 case Jret0:
495 if (fn->dynalloc) {
496 if (frame - 16 <= 2048)
497 fprintf(f,
498 "\tadd sp, fp, -%d\n",
499 frame - 16
501 else
502 fprintf(f,
503 "\tli t6, %d\n"
504 "\tsub sp, fp, t6\n",
505 frame - 16
508 for (pr=rv64_rclob, off=0; *pr>=0; pr++) {
509 if (fn->reg & BIT(*pr)) {
510 fprintf(f,
511 "\t%s %s, %d(sp)\n",
512 *pr < FT0 ? "ld" : "fld",
513 rname[*pr], off
515 off += 8;
518 fprintf(f,
519 "\tadd sp, fp, %d\n"
520 "\tld ra, 8(fp)\n"
521 "\tld fp, 0(fp)\n"
522 "\tret\n",
523 16 + fn->vararg * 64
525 break;
526 case Jjmp:
527 Jmp:
528 if (b->s1 != b->link)
529 fprintf(f, "\tj .L%d\n", id0+b->s1->id);
530 else
531 lbl = 0;
532 break;
533 case Jjnz:
534 neg = 0;
535 if (b->link == b->s2) {
536 s = b->s1;
537 b->s1 = b->s2;
538 b->s2 = s;
539 neg = 1;
541 assert(isreg(b->jmp.arg));
542 fprintf(f,
543 "\tb%sz %s, .L%d\n",
544 neg ? "ne" : "eq",
545 rname[b->jmp.arg.val],
546 id0+b->s2->id
548 goto Jmp;
551 id0 += fn->nblk;