test: use architecture-neutral wrapper for calling vprintf
[qbe.git] / arm64 / emit.c
blobcd0a2b1ad2a0e73df4270a11b296a2f00877b8c3
1 #include "all.h"
3 typedef struct E E;
5 struct E {
6 FILE *f;
7 Fn *fn;
8 uint64_t frame;
9 uint padding;
12 #define CMP(X) \
13 X(Cieq, "eq") \
14 X(Cine, "ne") \
15 X(Cisge, "ge") \
16 X(Cisgt, "gt") \
17 X(Cisle, "le") \
18 X(Cislt, "lt") \
19 X(Ciuge, "cs") \
20 X(Ciugt, "hi") \
21 X(Ciule, "ls") \
22 X(Ciult, "cc") \
23 X(NCmpI+Cfeq, "eq") \
24 X(NCmpI+Cfge, "ge") \
25 X(NCmpI+Cfgt, "gt") \
26 X(NCmpI+Cfle, "ls") \
27 X(NCmpI+Cflt, "mi") \
28 X(NCmpI+Cfne, "ne") \
29 X(NCmpI+Cfo, "vc") \
30 X(NCmpI+Cfuo, "vs")
32 enum {
33 Ki = -1, /* matches Kw and Kl */
34 Ka = -2, /* matches all classes */
37 static struct {
38 short op;
39 short cls;
40 char *asm;
41 } omap[] = {
42 { Oadd, Ki, "add %=, %0, %1" },
43 { Oadd, Ka, "fadd %=, %0, %1" },
44 { Osub, Ki, "sub %=, %0, %1" },
45 { Osub, Ka, "fsub %=, %0, %1" },
46 { Oand, Ki, "and %=, %0, %1" },
47 { Oor, Ki, "orr %=, %0, %1" },
48 { Oxor, Ki, "eor %=, %0, %1" },
49 { Osar, Ki, "asr %=, %0, %1" },
50 { Oshr, Ki, "lsr %=, %0, %1" },
51 { Oshl, Ki, "lsl %=, %0, %1" },
52 { Omul, Ki, "mul %=, %0, %1" },
53 { Omul, Ka, "fmul %=, %0, %1" },
54 { Odiv, Ki, "sdiv %=, %0, %1" },
55 { Odiv, Ka, "fdiv %=, %0, %1" },
56 { Oudiv, Ki, "udiv %=, %0, %1" },
57 { Orem, Ki, "sdiv %?, %0, %1\n\tmsub\t%=, %?, %1, %0" },
58 { Ourem, Ki, "udiv %?, %0, %1\n\tmsub\t%=, %?, %1, %0" },
59 { Ocopy, Ki, "mov %=, %0" },
60 { Ocopy, Ka, "fmov %=, %0" },
61 { Oswap, Ki, "mov %?, %0\n\tmov\t%0, %1\n\tmov\t%1, %?" },
62 { Oswap, Ka, "fmov %?, %0\n\tfmov\t%0, %1\n\tfmov\t%1, %?" },
63 { Ostoreb, Kw, "strb %W0, %M1" },
64 { Ostoreh, Kw, "strh %W0, %M1" },
65 { Ostorew, Kw, "str %W0, %M1" },
66 { Ostorel, Kw, "str %L0, %M1" },
67 { Ostores, Kw, "str %S0, %M1" },
68 { Ostored, Kw, "str %D0, %M1" },
69 { Oloadsb, Ki, "ldrsb %=, %M0" },
70 { Oloadub, Ki, "ldrb %W=, %M0" },
71 { Oloadsh, Ki, "ldrsh %=, %M0" },
72 { Oloaduh, Ki, "ldrh %W=, %M0" },
73 { Oloadsw, Kw, "ldr %=, %M0" },
74 { Oloadsw, Kl, "ldrsw %=, %M0" },
75 { Oloaduw, Ki, "ldr %W=, %M0" },
76 { Oload, Ka, "ldr %=, %M0" },
77 { Oextsb, Ki, "sxtb %=, %W0" },
78 { Oextub, Ki, "uxtb %W=, %W0" },
79 { Oextsh, Ki, "sxth %=, %W0" },
80 { Oextuh, Ki, "uxth %W=, %W0" },
81 { Oextsw, Ki, "sxtw %L=, %W0" },
82 { Oextuw, Ki, "mov %W=, %W0" },
83 { Oexts, Kd, "fcvt %=, %S0" },
84 { Otruncd, Ks, "fcvt %=, %D0" },
85 { Ocast, Kw, "fmov %=, %S0" },
86 { Ocast, Kl, "fmov %=, %D0" },
87 { Ocast, Ks, "fmov %=, %W0" },
88 { Ocast, Kd, "fmov %=, %L0" },
89 { Ostosi, Ka, "fcvtzs %=, %S0" },
90 { Odtosi, Ka, "fcvtzs %=, %D0" },
91 { Oswtof, Ka, "scvtf %=, %W0" },
92 { Osltof, Ka, "scvtf %=, %L0" },
93 { Ocall, Kw, "blr %L0" },
95 { Oacmp, Ki, "cmp %0, %1" },
96 { Oacmn, Ki, "cmn %0, %1" },
97 { Oafcmp, Ka, "fcmpe %0, %1" },
99 #define X(c, str) \
100 { Oflag+c, Ki, "cset %=, " str },
101 CMP(X)
102 #undef X
103 { NOp, 0, 0 }
106 static char *
107 rname(int r, int k)
109 static char buf[4];
111 if (r == SP) {
112 assert(k == Kl);
113 sprintf(buf, "sp");
115 else if (R0 <= r && r <= LR)
116 switch (k) {
117 default: die("invalid class");
118 case Kw: sprintf(buf, "w%d", r-R0); break;
119 case Kx:
120 case Kl: sprintf(buf, "x%d", r-R0); break;
122 else if (V0 <= r && r <= V30)
123 switch (k) {
124 default: die("invalid class");
125 case Ks: sprintf(buf, "s%d", r-V0); break;
126 case Kx:
127 case Kd: sprintf(buf, "d%d", r-V0); break;
129 else
130 die("invalid register");
131 return buf;
134 static uint64_t
135 slot(int s, E *e)
137 s = ((int32_t)s << 3) >> 3;
138 if (s == -1)
139 return 16 + e->frame;
140 if (s < 0) {
141 if (e->fn->vararg)
142 return 16 + e->frame + 192 - (s+2)*8;
143 else
144 return 16 + e->frame - (s+2)*8;
145 } else
146 return 16 + e->padding + 4 * s;
149 static void
150 emitf(char *s, Ins *i, E *e)
152 Ref r;
153 int k, c;
154 Con *pc;
155 unsigned n, sp;
157 fputc('\t', e->f);
159 sp = 0;
160 for (;;) {
161 k = i->cls;
162 while ((c = *s++) != '%')
163 if (c == ' ' && !sp) {
164 fputc('\t', e->f);
165 sp = 1;
166 } else if ( !c) {
167 fputc('\n', e->f);
168 return;
169 } else
170 fputc(c, e->f);
171 Switch:
172 switch ((c = *s++)) {
173 default:
174 die("invalid escape");
175 case 'W':
176 k = Kw;
177 goto Switch;
178 case 'L':
179 k = Kl;
180 goto Switch;
181 case 'S':
182 k = Ks;
183 goto Switch;
184 case 'D':
185 k = Kd;
186 goto Switch;
187 case '?':
188 if (KBASE(k) == 0)
189 fputs(rname(R18, k), e->f);
190 else
191 fputs(k==Ks ? "s31" : "d31", e->f);
192 break;
193 case '=':
194 case '0':
195 r = c == '=' ? i->to : i->arg[0];
196 assert(isreg(r));
197 fputs(rname(r.val, k), e->f);
198 break;
199 case '1':
200 r = i->arg[1];
201 switch (rtype(r)) {
202 default:
203 die("invalid second argument");
204 case RTmp:
205 assert(isreg(r));
206 fputs(rname(r.val, k), e->f);
207 break;
208 case RCon:
209 pc = &e->fn->con[r.val];
210 n = pc->bits.i;
211 assert(pc->type == CBits);
212 if (n & 0xfff000)
213 fprintf(e->f, "#%u, lsl #12", n>>12);
214 else
215 fprintf(e->f, "#%u", n);
216 break;
218 break;
219 case 'M':
220 c = *s++;
221 assert(c == '0' || c == '1');
222 r = i->arg[c - '0'];
223 assert(isreg(r) && "TODO emit non reg addresses");
224 fprintf(e->f, "[%s]", rname(r.val, Kl));
225 break;
230 static void
231 loadcon(Con *c, int r, int k, FILE *f)
233 char *rn, *p, off[32];
234 int64_t n;
235 int w, sh;
237 w = KWIDE(k);
238 rn = rname(r, k);
239 n = c->bits.i;
240 if (c->type == CAddr) {
241 rn = rname(r, Kl);
242 if (n)
243 sprintf(off, "+%"PRIi64, n);
244 else
245 off[0] = 0;
246 p = c->local ? ".L" : "";
247 fprintf(f, "\tadrp\t%s, %s%s%s\n",
248 rn, p, str(c->label), off);
249 fprintf(f, "\tadd\t%s, %s, #:lo12:%s%s%s\n",
250 rn, rn, p, str(c->label), off);
251 return;
253 assert(c->type == CBits);
254 if (!w)
255 n = (int32_t)n;
256 if ((n | 0xffff) == -1 || arm64_logimm(n, k)) {
257 fprintf(f, "\tmov\t%s, #%"PRIi64"\n", rn, n);
258 } else {
259 fprintf(f, "\tmov\t%s, #%d\n",
260 rn, (int)(n & 0xffff));
261 for (sh=16; n>>=16; sh+=16) {
262 if ((!w && sh == 32) || sh == 64)
263 break;
264 fprintf(f, "\tmovk\t%s, #0x%x, lsl #%d\n",
265 rn, (unsigned)(n & 0xffff), sh);
270 static void
271 emitins(Ins *i, E *e)
273 int o;
274 char *rn;
275 uint64_t s;
277 switch (i->op) {
278 default:
279 Table:
280 /* most instructions are just pulled out of
281 * the table omap[], some special cases are
282 * detailed below */
283 for (o=0;; o++) {
284 /* this linear search should really be a binary
285 * search */
286 if (omap[o].op == NOp)
287 die("no match for %s(%c)",
288 optab[i->op].name, "wlsd"[i->cls]);
289 if (omap[o].op == i->op)
290 if (omap[o].cls == i->cls || omap[o].cls == Ka
291 || (omap[o].cls == Ki && KBASE(i->cls) == 0))
292 break;
294 emitf(omap[o].asm, i, e);
295 break;
296 case Onop:
297 break;
298 case Ocopy:
299 if (req(i->to, i->arg[0]))
300 break;
301 if (rtype(i->arg[0]) != RCon)
302 goto Table;
303 loadcon(&e->fn->con[i->arg[0].val], i->to.val, i->cls, e->f);
304 break;
305 case Oaddr:
306 assert(rtype(i->arg[0]) == RSlot);
307 rn = rname(i->to.val, Kl);
308 s = slot(i->arg[0].val, e);
309 if (s <= 4095) {
310 fprintf(e->f, "\tadd\t%s, x29, #%"PRIu64"\n", rn, s);
311 } else {
312 fprintf(e->f,
313 "\tmov\t%s, #%"PRIu64"\n"
314 "\tadd\t%s, x29, %s\n",
315 rn, s, rn, rn
318 break;
322 static void
323 framelayout(E *e)
325 int *r;
326 uint o;
327 uint64_t f;
329 for (o=0, r=arm64_rclob; *r>=0; r++)
330 o += 1 & (e->fn->reg >> *r);
331 f = e->fn->slot;
332 f = (f + 3) & -4;
333 o += o & 1;
334 e->padding = 4*(f-e->fn->slot);
335 e->frame = 4*f + 8*o;
340 Stack-frame layout:
342 +=============+
343 | varargs |
344 | save area |
345 +-------------+
346 | callee-save | ^
347 | registers | |
348 +-------------+ |
349 | ... | |
350 | spill slots | |
351 | ... | | e->frame
352 +-------------+ |
353 | ... | |
354 | locals | |
355 | ... | |
356 +-------------+ |
357 | e->padding | v
358 +-------------+
359 | saved x29 |
360 | saved x30 |
361 +=============+ <- x29
365 void
366 arm64_emitfn(Fn *fn, FILE *out)
368 static char *ctoa[] = {
369 #define X(c, s) [c] = s,
370 CMP(X)
371 #undef X
373 static int id0;
374 int n, c, lbl, *r;
375 uint64_t o;
376 Blk *b, *s;
377 Ins *i;
378 E *e;
380 e = &(E){.f = out, .fn = fn};
381 framelayout(e);
383 fprintf(e->f, ".text\n");
384 if (e->fn->export)
385 fprintf(e->f, ".globl %s\n", e->fn->name);
386 fprintf(e->f, "%s:\n", e->fn->name);
388 if (e->fn->vararg) {
389 for (n=7; n>=0; n--)
390 fprintf(e->f, "\tstr\tq%d, [sp, -16]!\n", n);
391 for (n=7; n>=0; n-=2)
392 fprintf(e->f, "\tstp\tx%d, x%d, [sp, -16]!\n", n-1, n);
395 if (e->frame + 16 <= 512)
396 fprintf(e->f,
397 "\tstp\tx29, x30, [sp, -%"PRIu64"]!\n",
398 e->frame + 16
400 else if (e->frame <= 4095)
401 fprintf(e->f,
402 "\tsub\tsp, sp, #%"PRIu64"\n"
403 "\tstp\tx29, x30, [sp, -16]!\n",
404 e->frame
406 else
407 fprintf(e->f,
408 "\tmov\tx16, #%"PRIu64"\n"
409 "\tsub\tsp, sp, x16\n"
410 "\tstp\tx29, x30, [sp, -16]!\n",
411 e->frame
413 fputs("\tadd\tx29, sp, 0\n", e->f);
414 for (o=e->frame+16, r=arm64_rclob; *r>=0; r++)
415 if (e->fn->reg & BIT(*r))
416 fprintf(e->f,
417 "\tstr\t%s, [sp, %"PRIu64"]\n",
418 rname(*r, Kx), o -= 8
421 for (lbl=0, b=e->fn->start; b; b=b->link) {
422 if (lbl || b->npred > 1)
423 fprintf(e->f, ".L%d:\n", id0+b->id);
424 for (i=b->ins; i!=&b->ins[b->nins]; i++)
425 emitins(i, e);
426 lbl = 1;
427 switch (b->jmp.type) {
428 case Jret0:
429 for (o=e->frame+16, r=arm64_rclob; *r>=0; r++)
430 if (e->fn->reg & BIT(*r))
431 fprintf(e->f,
432 "\tldr\t%s, [sp, %"PRIu64"]\n",
433 rname(*r, Kx), o -= 8
435 o = e->frame + 16;
436 if (e->fn->vararg)
437 o += 192;
438 if (o <= 504)
439 fprintf(e->f,
440 "\tldp\tx29, x30, [sp], %"PRIu64"\n",
443 else if (o - 16 <= 4095)
444 fprintf(e->f,
445 "\tldp\tx29, x30, [sp], 16\n"
446 "\tadd\tsp, sp, #%"PRIu64"\n",
447 o - 16
449 else
450 fprintf(e->f,
451 "\tldp\tx29, x30, [sp], 16\n"
452 "\tmov\tx16, #%"PRIu64"\n"
453 "\tadd\tsp, sp, x16\n",
454 o - 16
456 fprintf(e->f, "\tret\n");
457 break;
458 case Jjmp:
459 Jmp:
460 if (b->s1 != b->link)
461 fprintf(e->f, "\tb\t.L%d\n", id0+b->s1->id);
462 else
463 lbl = 0;
464 break;
465 default:
466 c = b->jmp.type - Jjf;
467 if (c < 0 || c > NCmp)
468 die("unhandled jump %d", b->jmp.type);
469 if (b->link == b->s2) {
470 s = b->s1;
471 b->s1 = b->s2;
472 b->s2 = s;
473 } else
474 c = cmpneg(c);
475 fprintf(e->f, "\tb%s\t.L%d\n", ctoa[c], id0+b->s2->id);
476 goto Jmp;
479 id0 += e->fn->nblk;