prefer bigger amd64 addressing
[qbe.git] / arm64 / emit.c
blob8211c4f84ca57efcf36db2894f6b730f0c65534c
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 %=, %M0" },
71 { Oloadsh, Ki, "ldrsh %=, %M0" },
72 { Oloaduh, Ki, "ldrh %=, %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 { Ocast, Kw, "fmov %=, %S0" },
85 { Ocast, Kl, "fmov %=, %D0" },
86 { Ocast, Ks, "fmov %=, %W0" },
87 { Ocast, Kd, "fmov %=, %L0" },
88 { Ostosi, Ka, "fcvtzs %=, %S0" },
89 { Odtosi, Ka, "fcvtzs %=, %D0" },
90 { Oswtof, Ka, "scvtf %=, %W0" },
91 { Osltof, Ka, "scvtf %=, %L0" },
92 { Ocall, Kw, "blr %L0" },
94 { Oacmp, Ki, "cmp %0, %1" },
95 { Oacmn, Ki, "cmn %0, %1" },
96 { Oafcmp, Ka, "fcmpe %0, %1" },
98 #define X(c, str) \
99 { Oflag+c, Ki, "cset %=, " str },
100 CMP(X)
101 #undef X
102 { NOp, 0, 0 }
105 static char *
106 rname(int r, int k)
108 static char buf[4];
110 if (r == SP) {
111 assert(k == Kl);
112 sprintf(buf, "sp");
114 else if (R0 <= r && r <= LR)
115 switch (k) {
116 default: die("invalid class");
117 case Kw: sprintf(buf, "w%d", r-R0); break;
118 case Kx:
119 case Kl: sprintf(buf, "x%d", r-R0); break;
121 else if (V0 <= r && r <= V30)
122 switch (k) {
123 default: die("invalid class");
124 case Ks: sprintf(buf, "s%d", r-V0); break;
125 case Kx:
126 case Kd: sprintf(buf, "d%d", r-V0); break;
128 else
129 die("invalid register");
130 return buf;
133 static uint64_t
134 slot(int s, E *e)
136 s = ((int32_t)s << 3) >> 3;
137 if (s == -1)
138 return 16 + e->frame;
139 if (s < 0) {
140 if (e->fn->vararg)
141 return 16 + e->frame + 192 - (s+2)*8;
142 else
143 return 16 + e->frame - (s+2)*8;
144 } else
145 return 16 + e->padding + 4 * s;
148 static void
149 emitf(char *s, Ins *i, E *e)
151 Ref r;
152 int k, c;
153 Con *pc;
154 unsigned n, sp;
156 fputc('\t', e->f);
158 sp = 0;
159 for (;;) {
160 k = i->cls;
161 while ((c = *s++) != '%')
162 if (c == ' ' && !sp) {
163 fputc('\t', e->f);
164 sp = 1;
165 } else if ( !c) {
166 fputc('\n', e->f);
167 return;
168 } else
169 fputc(c, e->f);
170 Switch:
171 switch ((c = *s++)) {
172 default:
173 die("invalid escape");
174 case 'W':
175 k = Kw;
176 goto Switch;
177 case 'L':
178 k = Kl;
179 goto Switch;
180 case 'S':
181 k = Ks;
182 goto Switch;
183 case 'D':
184 k = Kd;
185 goto Switch;
186 case '?':
187 if (KBASE(k) == 0)
188 fputs(rname(R18, k), e->f);
189 else
190 fputs(k==Ks ? "s31" : "d31", e->f);
191 break;
192 case '=':
193 case '0':
194 r = c == '=' ? i->to : i->arg[0];
195 assert(isreg(r));
196 fputs(rname(r.val, k), e->f);
197 break;
198 case '1':
199 r = i->arg[1];
200 switch (rtype(r)) {
201 default:
202 die("invalid second argument");
203 case RTmp:
204 assert(isreg(r));
205 fputs(rname(r.val, k), e->f);
206 break;
207 case RCon:
208 pc = &e->fn->con[r.val];
209 n = pc->bits.i;
210 assert(pc->type == CBits);
211 if (n & 0xfff000)
212 fprintf(e->f, "#%u, lsl #12", n>>12);
213 else
214 fprintf(e->f, "#%u", n);
215 break;
217 break;
218 case 'M':
219 c = *s++;
220 assert(c == '0' || c == '1');
221 r = i->arg[c - '0'];
222 assert(isreg(r) && "TODO emit non reg addresses");
223 fprintf(e->f, "[%s]", rname(r.val, Kl));
224 break;
229 static void
230 loadcon(Con *c, int r, int k, FILE *f)
232 char *rn, *p, off[32];
233 int64_t n;
234 int w, sh;
236 w = KWIDE(k);
237 rn = rname(r, k);
238 n = c->bits.i;
239 if (c->type == CAddr) {
240 rn = rname(r, Kl);
241 if (n)
242 sprintf(off, "+%"PRIi64, n);
243 else
244 off[0] = 0;
245 p = c->local ? ".L" : "";
246 fprintf(f, "\tadrp\t%s, %s%s%s\n",
247 rn, p, str(c->label), off);
248 fprintf(f, "\tadd\t%s, %s, #:lo12:%s%s%s\n",
249 rn, rn, p, str(c->label), off);
250 return;
252 assert(c->type == CBits);
253 if (!w)
254 n = (int32_t)n;
255 if ((n | 0xffff) == -1 || arm64_logimm(n, k)) {
256 fprintf(f, "\tmov\t%s, #%"PRIi64"\n", rn, n);
257 } else {
258 fprintf(f, "\tmov\t%s, #%d\n",
259 rn, (int)(n & 0xffff));
260 for (sh=16; n>>=16; sh+=16) {
261 if ((!w && sh == 32) || sh == 64)
262 break;
263 fprintf(f, "\tmovk\t%s, #0x%x, lsl #%d\n",
264 rn, (unsigned)(n & 0xffff), sh);
269 static void
270 emitins(Ins *i, E *e)
272 int o;
274 switch (i->op) {
275 default:
276 Table:
277 /* most instructions are just pulled out of
278 * the table omap[], some special cases are
279 * detailed below */
280 for (o=0;; o++) {
281 /* this linear search should really be a binary
282 * search */
283 if (omap[o].op == NOp)
284 die("no match for %s(%c)",
285 optab[i->op].name, "wlsd"[i->cls]);
286 if (omap[o].op == i->op)
287 if (omap[o].cls == i->cls || omap[o].cls == Ka
288 || (omap[o].cls == Ki && KBASE(i->cls) == 0))
289 break;
291 emitf(omap[o].asm, i, e);
292 break;
293 case Onop:
294 break;
295 case Ocopy:
296 if (req(i->to, i->arg[0]))
297 break;
298 if (rtype(i->arg[0]) != RCon)
299 goto Table;
300 loadcon(&e->fn->con[i->arg[0].val], i->to.val, i->cls, e->f);
301 break;
302 case Oaddr:
303 assert(rtype(i->arg[0]) == RSlot);
304 fprintf(e->f, "\tadd\t%s, x29, #%"PRIu64"\n",
305 rname(i->to.val, Kl), slot(i->arg[0].val, e)
307 break;
311 static void
312 framelayout(E *e)
314 int *r;
315 uint o;
316 uint64_t f;
318 for (o=0, r=arm64_rclob; *r>=0; r++)
319 o += 1 & (e->fn->reg >> *r);
320 f = e->fn->slot;
321 f = (f + 3) & -4;
322 o += o & 1;
323 e->padding = 4*(f-e->fn->slot);
324 e->frame = 4*f + 8*o;
329 Stack-frame layout:
331 +=============+
332 | varargs |
333 | save area |
334 +-------------+
335 | callee-save | ^
336 | registers | |
337 +-------------+ |
338 | ... | |
339 | spill slots | |
340 | ... | | e->frame
341 +-------------+ |
342 | ... | |
343 | locals | |
344 | ... | |
345 +-------------+ |
346 | e->padding | v
347 +-------------+
348 | saved x29 |
349 | saved x30 |
350 +=============+ <- x29
354 void
355 arm64_emitfn(Fn *fn, FILE *out)
357 static char *ctoa[] = {
358 #define X(c, s) [c] = s,
359 CMP(X)
360 #undef X
362 static int id0;
363 int n, c, lbl, *r;
364 uint64_t o;
365 Blk *b, *s;
366 Ins *i;
367 E *e;
369 e = &(E){.f = out, .fn = fn};
370 framelayout(e);
372 fprintf(e->f, ".text\n");
373 if (e->fn->export)
374 fprintf(e->f, ".globl %s\n", e->fn->name);
375 fprintf(e->f, "%s:\n", e->fn->name);
377 if (e->fn->vararg) {
378 for (n=7; n>=0; n--)
379 fprintf(e->f, "\tstr\tq%d, [sp, -16]!\n", n);
380 for (n=7; n>=0; n--)
381 fprintf(e->f, "\tstr\tx%d, [sp, -8]!\n", n);
384 if (e->frame + 16 > 512)
385 fprintf(e->f,
386 "\tsub\tsp, sp, #%"PRIu64"\n"
387 "\tstp\tx29, x30, [sp, -16]!\n",
388 e->frame
390 else
391 fprintf(e->f,
392 "\tstp\tx29, x30, [sp, -%"PRIu64"]!\n",
393 e->frame + 16
395 fputs("\tadd\tx29, sp, 0\n", e->f);
396 for (o=e->frame+16, r=arm64_rclob; *r>=0; r++)
397 if (e->fn->reg & BIT(*r))
398 fprintf(e->f,
399 "\tstr\t%s, [sp, %"PRIu64"]\n",
400 rname(*r, Kx), o -= 8
403 for (lbl=0, b=e->fn->start; b; b=b->link) {
404 if (lbl || b->npred > 1)
405 fprintf(e->f, ".L%d:\n", id0+b->id);
406 for (i=b->ins; i!=&b->ins[b->nins]; i++)
407 emitins(i, e);
408 lbl = 1;
409 switch (b->jmp.type) {
410 case Jret0:
411 for (o=e->frame+16, r=arm64_rclob; *r>=0; r++)
412 if (e->fn->reg & BIT(*r))
413 fprintf(e->f,
414 "\tldr\t%s, [sp, %"PRIu64"]\n",
415 rname(*r, Kx), o -= 8
417 o = e->frame + 16;
418 if (e->fn->vararg)
419 o += 192;
420 if (o > 504)
421 fprintf(e->f,
422 "\tldp\tx29, x30, [sp], 16\n"
423 "\tadd\tsp, sp, #%"PRIu64"\n",
424 o - 16
426 else
427 fprintf(e->f,
428 "\tldp\tx29, x30, [sp], %"PRIu64"\n",
431 fprintf(e->f, "\tret\n");
432 break;
433 case Jjmp:
434 Jmp:
435 if (b->s1 != b->link)
436 fprintf(e->f, "\tb\t.L%d\n", id0+b->s1->id);
437 else
438 lbl = 0;
439 break;
440 default:
441 c = b->jmp.type - Jjf;
442 if (c < 0 || c > NCmp)
443 die("unhandled jump %d", b->jmp.type);
444 if (b->link == b->s2) {
445 s = b->s1;
446 b->s1 = b->s2;
447 b->s2 = s;
448 } else
449 c = cmpneg(c);
450 fprintf(e->f, "\tb%s\t.L%d\n", ctoa[c], id0+b->s2->id);
451 goto Jmp;
454 id0 += e->fn->nblk;