replace asm keyword
[qbe.git] / rv64 / emit.c
blob39b55ea4c13c09d161702b078215abe229eeffe9
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 *fmt;
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(Ref r, Fn *fn)
121 int s;
123 s = rsval(r);
124 assert(s <= fn->slot);
125 if (s < 0)
126 return 8 * -s;
127 else
128 return -4 * (fn->slot - s);
131 static void
132 emitaddr(Con *c, FILE *f)
134 assert(c->sym.type == SGlo);
135 fputs(str(c->sym.id), f);
136 if (c->bits.i)
137 fprintf(f, "+%"PRIi64, c->bits.i);
140 static void
141 emitf(char *s, Ins *i, Fn *fn, FILE *f)
143 static char clschr[] = {'w', 'l', 's', 'd'};
144 Ref r;
145 int k, c;
146 Con *pc;
147 int64_t offset;
149 fputc('\t', f);
150 for (;;) {
151 k = i->cls;
152 while ((c = *s++) != '%')
153 if (!c) {
154 fputc('\n', f);
155 return;
156 } else
157 fputc(c, f);
158 switch ((c = *s++)) {
159 default:
160 die("invalid escape");
161 case '?':
162 if (KBASE(k) == 0)
163 fputs("t6", f);
164 else
165 fputs("ft11", f);
166 break;
167 case 'k':
168 if (i->cls != Kl)
169 fputc(clschr[i->cls], f);
170 break;
171 case '=':
172 case '0':
173 r = c == '=' ? i->to : i->arg[0];
174 assert(isreg(r));
175 fputs(rname[r.val], f);
176 break;
177 case '1':
178 r = i->arg[1];
179 switch (rtype(r)) {
180 default:
181 die("invalid second argument");
182 case RTmp:
183 assert(isreg(r));
184 fputs(rname[r.val], f);
185 break;
186 case RCon:
187 pc = &fn->con[r.val];
188 assert(pc->type == CBits);
189 assert(pc->bits.i >= -2048 && pc->bits.i < 2048);
190 fprintf(f, "%d", (int)pc->bits.i);
191 break;
193 break;
194 case 'M':
195 c = *s++;
196 assert(c == '0' || c == '1');
197 r = i->arg[c - '0'];
198 switch (rtype(r)) {
199 default:
200 die("invalid address argument");
201 case RTmp:
202 fprintf(f, "0(%s)", rname[r.val]);
203 break;
204 case RCon:
205 pc = &fn->con[r.val];
206 assert(pc->type == CAddr);
207 emitaddr(pc, f);
208 if (isstore(i->op)
209 || (isload(i->op) && KBASE(i->cls) == 1)) {
210 /* store (and float load)
211 * pseudo-instructions need a
212 * temporary register in which to
213 * load the address
215 fprintf(f, ", t6");
217 break;
218 case RSlot:
219 offset = slot(r, fn);
220 assert(offset >= -2048 && offset <= 2047);
221 fprintf(f, "%d(fp)", (int)offset);
222 break;
224 break;
229 static void
230 loadaddr(Con *c, char *rn, FILE *f)
232 char off[32];
234 if (c->sym.type == SThr) {
235 if (c->bits.i)
236 sprintf(off, "+%"PRIi64, c->bits.i);
237 else
238 off[0] = 0;
239 fprintf(f, "\tlui %s, %%tprel_hi(%s)%s\n",
240 rn, str(c->sym.id), off);
241 fprintf(f, "\tadd %s, %s, tp, %%tprel_add(%s)%s\n",
242 rn, rn, str(c->sym.id), off);
243 fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n",
244 rn, rn, str(c->sym.id), off);
245 } else {
246 fprintf(f, "\tla %s, ", rn);
247 emitaddr(c, f);
248 fputc('\n', f);
252 static void
253 loadcon(Con *c, int r, int k, FILE *f)
255 char *rn;
256 int64_t n;
258 rn = rname[r];
259 switch (c->type) {
260 case CAddr:
261 loadaddr(c, rn, f);
262 break;
263 case CBits:
264 n = c->bits.i;
265 if (!KWIDE(k))
266 n = (int32_t)n;
267 fprintf(f, "\tli %s, %"PRIi64"\n", rn, n);
268 break;
269 default:
270 die("invalid constant");
274 static void
275 fixmem(Ref *pr, Fn *fn, FILE *f)
277 Ref r;
278 int64_t s;
279 Con *c;
281 r = *pr;
282 if (rtype(r) == RCon) {
283 c = &fn->con[r.val];
284 if (c->type == CAddr)
285 if (c->sym.type == SThr) {
286 loadcon(c, T6, Kl, f);
287 *pr = TMP(T6);
290 if (rtype(r) == RSlot) {
291 s = slot(r, fn);
292 if (s < -2048 || s > 2047) {
293 fprintf(f, "\tli t6, %"PRId64"\n", s);
294 fprintf(f, "\tadd t6, fp, t6\n");
295 *pr = TMP(T6);
300 static void
301 emitins(Ins *i, Fn *fn, FILE *f)
303 int o;
304 char *rn;
305 int64_t s;
306 Con *con;
308 switch (i->op) {
309 default:
310 if (isload(i->op))
311 fixmem(&i->arg[0], fn, f);
312 else if (isstore(i->op))
313 fixmem(&i->arg[1], fn, f);
314 Table:
315 /* most instructions are just pulled out of
316 * the table omap[], some special cases are
317 * detailed below */
318 for (o=0;; o++) {
319 /* this linear search should really be a binary
320 * search */
321 if (omap[o].op == NOp)
322 die("no match for %s(%c)",
323 optab[i->op].name, "wlsd"[i->cls]);
324 if (omap[o].op == i->op)
325 if (omap[o].cls == i->cls || omap[o].cls == Ka
326 || (omap[o].cls == Ki && KBASE(i->cls) == 0))
327 break;
329 emitf(omap[o].fmt, i, fn, f);
330 break;
331 case Ocopy:
332 if (req(i->to, i->arg[0]))
333 break;
334 if (rtype(i->to) == RSlot) {
335 switch (rtype(i->arg[0])) {
336 case RSlot:
337 case RCon:
338 die("unimplemented");
339 break;
340 default:
341 assert(isreg(i->arg[0]));
342 i->arg[1] = i->to;
343 i->to = R;
344 switch (i->cls) {
345 case Kw: i->op = Ostorew; break;
346 case Kl: i->op = Ostorel; break;
347 case Ks: i->op = Ostores; break;
348 case Kd: i->op = Ostored; break;
350 fixmem(&i->arg[1], fn, f);
351 goto Table;
353 break;
355 assert(isreg(i->to));
356 switch (rtype(i->arg[0])) {
357 case RCon:
358 loadcon(&fn->con[i->arg[0].val], i->to.val, i->cls, f);
359 break;
360 case RSlot:
361 i->op = Oload;
362 fixmem(&i->arg[0], fn, f);
363 goto Table;
364 default:
365 assert(isreg(i->arg[0]));
366 goto Table;
368 break;
369 case Onop:
370 break;
371 case Oaddr:
372 assert(rtype(i->arg[0]) == RSlot);
373 rn = rname[i->to.val];
374 s = slot(i->arg[0], fn);
375 if (-s < 2048) {
376 fprintf(f, "\tadd %s, fp, %"PRId64"\n", rn, s);
377 } else {
378 fprintf(f,
379 "\tli %s, %"PRId64"\n"
380 "\tadd %s, fp, %s\n",
381 rn, s, rn, rn
384 break;
385 case Ocall:
386 switch (rtype(i->arg[0])) {
387 case RCon:
388 con = &fn->con[i->arg[0].val];
389 if (con->type != CAddr
390 || con->sym.type != SGlo
391 || con->bits.i)
392 goto Invalid;
393 fprintf(f, "\tcall %s\n", str(con->sym.id));
394 break;
395 case RTmp:
396 emitf("jalr %0", i, fn, f);
397 break;
398 default:
399 Invalid:
400 die("invalid call argument");
402 break;
403 case Osalloc:
404 emitf("sub sp, sp, %0", i, fn, f);
405 if (!req(i->to, R))
406 emitf("mv %=, sp", i, fn, f);
407 break;
408 case Odbgloc:
409 emitdbgloc(i->arg[0].val, i->arg[1].val, f);
410 break;
416 Stack-frame layout:
418 +=============+
419 | varargs |
420 | save area |
421 +-------------+
422 | saved ra |
423 | saved fp |
424 +-------------+ <- fp
425 | ... |
426 | spill slots |
427 | ... |
428 +-------------+
429 | ... |
430 | locals |
431 | ... |
432 +-------------+
433 | padding |
434 +-------------+
435 | callee-save |
436 | registers |
437 +=============+
441 void
442 rv64_emitfn(Fn *fn, FILE *f)
444 static int id0;
445 int lbl, neg, off, frame, *pr, r;
446 Blk *b, *s;
447 Ins *i;
449 emitfnlnk(fn->name, &fn->lnk, f);
451 if (fn->vararg) {
452 /* TODO: only need space for registers
453 * unused by named arguments
455 fprintf(f, "\tadd sp, sp, -64\n");
456 for (r=A0; r<=A7; r++)
457 fprintf(f,
458 "\tsd %s, %d(sp)\n",
459 rname[r], 8 * (r - A0)
462 fprintf(f, "\tsd fp, -16(sp)\n");
463 fprintf(f, "\tsd ra, -8(sp)\n");
464 fprintf(f, "\tadd fp, sp, -16\n");
466 frame = (16 + 4 * fn->slot + 15) & ~15;
467 for (pr=rv64_rclob; *pr>=0; pr++) {
468 if (fn->reg & BIT(*pr))
469 frame += 8;
471 frame = (frame + 15) & ~15;
473 if (frame <= 2048)
474 fprintf(f,
475 "\tadd sp, sp, -%d\n",
476 frame
478 else
479 fprintf(f,
480 "\tli t6, %d\n"
481 "\tsub sp, sp, t6\n",
482 frame
484 for (pr=rv64_rclob, off=0; *pr>=0; pr++) {
485 if (fn->reg & BIT(*pr)) {
486 fprintf(f,
487 "\t%s %s, %d(sp)\n",
488 *pr < FT0 ? "sd" : "fsd",
489 rname[*pr], off
491 off += 8;
495 for (lbl=0, b=fn->start; b; b=b->link) {
496 if (lbl || b->npred > 1)
497 fprintf(f, ".L%d:\n", id0+b->id);
498 for (i=b->ins; i!=&b->ins[b->nins]; i++)
499 emitins(i, fn, f);
500 lbl = 1;
501 switch (b->jmp.type) {
502 case Jhlt:
503 fprintf(f, "\tebreak\n");
504 break;
505 case Jret0:
506 if (fn->dynalloc) {
507 if (frame - 16 <= 2048)
508 fprintf(f,
509 "\tadd sp, fp, -%d\n",
510 frame - 16
512 else
513 fprintf(f,
514 "\tli t6, %d\n"
515 "\tsub sp, fp, t6\n",
516 frame - 16
519 for (pr=rv64_rclob, off=0; *pr>=0; pr++) {
520 if (fn->reg & BIT(*pr)) {
521 fprintf(f,
522 "\t%s %s, %d(sp)\n",
523 *pr < FT0 ? "ld" : "fld",
524 rname[*pr], off
526 off += 8;
529 fprintf(f,
530 "\tadd sp, fp, %d\n"
531 "\tld ra, 8(fp)\n"
532 "\tld fp, 0(fp)\n"
533 "\tret\n",
534 16 + fn->vararg * 64
536 break;
537 case Jjmp:
538 Jmp:
539 if (b->s1 != b->link)
540 fprintf(f, "\tj .L%d\n", id0+b->s1->id);
541 else
542 lbl = 0;
543 break;
544 case Jjnz:
545 neg = 0;
546 if (b->link == b->s2) {
547 s = b->s1;
548 b->s1 = b->s2;
549 b->s2 = s;
550 neg = 1;
552 assert(isreg(b->jmp.arg));
553 fprintf(f,
554 "\tb%sz %s, .L%d\n",
555 neg ? "ne" : "eq",
556 rname[b->jmp.arg.val],
557 id0+b->s2->id
559 goto Jmp;
562 id0 += fn->nblk;
563 elf_emitfnfin(fn->name, f);