add funny 80 target
[qbe.git] / emit.c
blob6b624e070d84581039e659336e9e93c9be53da1c
1 #include "all.h"
3 char *locprefix, *symprefix;
5 enum {
6 SLong = 0,
7 SWord = 1,
8 SShort = 2,
9 SByte = 3,
11 Ki = -1, /* matches Kw and Kl */
12 Ka = -2, /* matches all classes */
15 /* Instruction format strings:
17 * if the format string starts with -, the instruction
18 * is assumed to be 3-address and is put in 2-address
19 * mode using an extra mov if necessary
21 * if the format string starts with +, the same as the
22 * above applies, but commutativity is also assumed
24 * %k is used to set the class of the instruction,
25 * it'll expand to "l", "q", "ss", "sd", depending
26 * on the instruction class
27 * %0 designates the first argument
28 * %1 designates the second argument
29 * %= designates the result
31 * if %k is not used, a prefix to 0, 1, or = must be
32 * added, it can be:
33 * M - memory reference
34 * L - long (64 bits)
35 * W - word (32 bits)
36 * H - short (16 bits)
37 * B - byte (8 bits)
38 * S - single precision float
39 * D - double precision float
41 static struct {
42 short op;
43 short cls;
44 char *asm;
45 } omap[] = {
46 { OAdd, Ka, "+add%k %1, %=" },
47 { OSub, Ka, "-sub%k %1, %=" },
48 { OAnd, Ki, "+and%k %1, %=" },
49 { OOr, Ki, "+or%k %1, %=" },
50 { OXor, Ki, "+xor%k %1, %=" },
51 { OSar, Ki, "-sar%k %B1, %=" },
52 { OShr, Ki, "-shr%k %B1, %=" },
53 { OShl, Ki, "-shl%k %B1, %=" },
54 { OMul, Ki, "+imul%k %1, %=" },
55 { OMul, Ks, "+mulss %1, %=" }, /* fixme */
56 { OMul, Kd, "+mulsd %1, %=" },
57 { ODiv, Ka, "-div%k %1, %=" },
58 { OStorel, Ka, "movq %L0, %M1" },
59 { OStorew, Ka, "movl %W0, %M1" },
60 { OStoreh, Ka, "movw %H0, %M1" },
61 { OStoreb, Ka, "movb %B0, %M1" },
62 { OStores, Ka, "movss %S0, %M1" },
63 { OStored, Ka, "movsd %D0, %M1" },
64 { OLoad, Ka, "mov%k %M0, %=" },
65 { OLoadsw, Kl, "movslq %M0, %L=" },
66 { OLoadsw, Kw, "movl %M0, %W=" },
67 { OLoaduw, Ki, "movl %M0, %W=" },
68 { OLoadsh, Ki, "movsw%k %M0, %=" },
69 { OLoaduh, Ki, "movzw%k %M0, %=" },
70 { OLoadsb, Ki, "movsb%k %M0, %=" },
71 { OLoadub, Ki, "movzb%k %M0, %=" },
72 { OExtsw, Kl, "movslq %W0, %L=" },
73 { OExtuw, Kl, "movl %W0, %W=" },
74 { OExtsh, Ki, "movsw%k %H0, %=" },
75 { OExtuh, Ki, "movzw%k %H0, %=" },
76 { OExtsb, Ki, "movsb%k %B0, %=" },
77 { OExtub, Ki, "movzb%k %B0, %=" },
79 { OExts, Kd, "cvtss2sd %0, %=" }, /* see if factorization is possible */
80 { OTruncd, Ks, "cvttsd2ss %0, %=" },
81 { OFtosi, Kw, "cvttss2si %0, %=" },
82 { OFtosi, Kl, "cvttsd2si %0, %=" },
83 { OSitof, Ks, "cvtsi2ss %W0, %=" },
84 { OSitof, Kd, "cvtsi2sd %L0, %=" },
85 { OCast, Ki, "movq %D0, %L=" },
86 { OCast, Ka, "movq %L0, %D=" },
88 { OAddr, Ki, "lea%k %M0, %=" },
89 { OSwap, Ki, "xchg%k %0, %1" },
90 { OSign, Kl, "cqto" },
91 { OSign, Kw, "cltd" },
92 { OXDiv, Ki, "div%k %0" },
93 { OXIDiv, Ki, "idiv%k %0" },
94 { OXCmp, Ks, "comiss %S0, %S1" }, /* fixme, Kf */
95 { OXCmp, Kd, "comisd %D0, %D1" },
96 { OXCmp, Ki, "cmp%k %0, %1" },
97 { OXTest, Ki, "test%k %0, %1" },
98 { OXSet+ICule, Ki, "setbe %B=\n\tmovzb%k %B=, %=" },
99 { OXSet+ICult, Ki, "setb %B=\n\tmovzb%k %B=, %=" },
100 { OXSet+ICsle, Ki, "setle %B=\n\tmovzb%k %B=, %=" },
101 { OXSet+ICslt, Ki, "setl %B=\n\tmovzb%k %B=, %=" },
102 { OXSet+ICsgt, Ki, "setg %B=\n\tmovzb%k %B=, %=" },
103 { OXSet+ICsge, Ki, "setge %B=\n\tmovzb%k %B=, %=" },
104 { OXSet+ICugt, Ki, "seta %B=\n\tmovzb%k %B=, %=" },
105 { OXSet+ICuge, Ki, "setae %B=\n\tmovzb%k %B=, %=" },
106 { OXSet+ICeq, Ki, "setz %B=\n\tmovzb%k %B=, %=" },
107 { OXSet+ICne, Ki, "setnz %B=\n\tmovzb%k %B=, %=" },
108 { OXSet+ICXnp, Ki, "setnp %B=\n\tmovsb%k %B=, %=" },
109 { OXSet+ICXp, Ki, "setp %B=\n\tmovsb%k %B=, %=" },
110 { NOp, 0, 0 }
113 static char *rname[][4] = {
114 [RAX] = {"rax", "eax", "ax", "al"},
115 [RBX] = {"rbx", "ebx", "bx", "bl"},
116 [RCX] = {"rcx", "ecx", "cx", "cl"},
117 [RDX] = {"rdx", "edx", "dx", "dl"},
118 [RSI] = {"rsi", "esi", "si", "sil"},
119 [RDI] = {"rdi", "edi", "di", "dil"},
120 [RBP] = {"rbp", "ebp", "bp", "bpl"},
121 [RSP] = {"rsp", "esp", "sp", "spl"},
122 [R8 ] = {"r8" , "r8d", "r8w", "r8b"},
123 [R9 ] = {"r9" , "r9d", "r9w", "r9b"},
124 [R10] = {"r10", "r10d", "r10w", "r10b"},
125 [R11] = {"r11", "r11d", "r11w", "r11b"},
126 [R12] = {"r12", "r12d", "r12w", "r12b"},
127 [R13] = {"r13", "r13d", "r13w", "r13b"},
128 [R14] = {"r14", "r14d", "r14w", "r14b"},
129 [R15] = {"r15", "r15d", "r15w", "r15b"},
133 static int
134 slot(int s, Fn *fn)
136 struct { int i:29; } x;
138 /* sign extend s using a bitfield */
139 x.i = s;
140 /* specific to NAlign == 3 */
141 if (x.i < 0)
142 return -4 * x.i;
143 else {
144 assert(fn->slot >= x.i);
145 return -4 * (fn->slot - x.i);
149 static void
150 emitcon(Con *con, FILE *f)
152 switch (con->type) {
153 case CAddr:
154 if (con->local)
155 fprintf(f, "%s%s", locprefix, con->label);
156 else
157 fprintf(f, "%s%s", symprefix, con->label);
158 if (con->bits.i)
159 fprintf(f, "%+"PRId64, con->bits.i);
160 break;
161 case CBits:
162 fprintf(f, "%"PRId64, con->bits.i);
163 break;
164 default:
165 die("unreachable");
169 static char *
170 regtoa(int reg, int sz)
172 static char buf[6];
174 if (reg >= XMM0) {
175 sprintf(buf, "xmm%d", reg-XMM0);
176 return buf;
177 } else
178 return rname[reg][sz];
181 static Ref
182 getarg(char c, Ins *i)
184 switch (c) {
185 case '0':
186 return i->arg[0];
187 case '1':
188 return i->arg[1];
189 case '=':
190 return i->to;
191 default:
192 die("invalid arg letter %c", c);
196 static void emitins(Ins, Fn *, FILE *);
198 static void
199 emitcopy(Ref r1, Ref r2, int k, Fn *fn, FILE *f)
201 Ins icp;
203 icp.op = OCopy;
204 icp.arg[0] = r2;
205 icp.to = r1;
206 icp.cls = k;
207 emitins(icp, fn, f);
210 static void
211 emitf(char *s, Ins *i, Fn *fn, FILE *f)
213 static char clstoa[][3] = {"l", "q", "ss", "sd"};
214 char c;
215 int sz;
216 Ref ref;
217 Mem *m;
218 Con off;
220 switch (*s) {
221 case '+':
222 if (req(i->arg[1], i->to)) {
223 ref = i->arg[0];
224 i->arg[0] = i->arg[1];
225 i->arg[1] = ref;
227 /* fall through */
228 case '-':
229 assert((!req(i->arg[1], i->to) || req(i->arg[0], i->to)) &&
230 "cannot convert to 2-address");
231 emitcopy(i->to, i->arg[0], i->cls, fn, f);
232 s++;
233 break;
236 fputc('\t', f);
237 Next:
238 while ((c = *s++) != '%')
239 if (!c) {
240 fputc('\n', f);
241 return;
242 } else
243 fputc(c, f);
244 switch ((c = *s++)) {
245 case '%':
246 fputc('%', f);
247 break;
248 case 'k':
249 fputs(clstoa[i->cls], f);
250 break;
251 case '0':
252 case '1':
253 case '=':
254 sz = KWIDE(i->cls) ? SLong : SWord;
255 s--;
256 goto Ref;
257 case 'D':
258 case 'S':
259 sz = SLong; /* does not matter for floats */
260 Ref:
261 c = *s++;
262 ref = getarg(c, i);
263 switch (rtype(ref)) {
264 case RTmp:
265 assert(isreg(ref));
266 fprintf(f, "%%%s", regtoa(ref.val, sz));
267 break;
268 case RSlot:
269 fprintf(f, "%d(%%rbp)", slot(ref.val, fn));
270 break;
271 case RMem:
272 Mem:
273 m = &fn->mem[ref.val];
274 if (rtype(m->base) == RSlot) {
275 off.type = CBits;
276 off.bits.i = slot(m->base.val, fn);
277 addcon(&m->offset, &off);
278 m->base = TMP(RBP);
280 if (m->offset.type != CUndef)
281 emitcon(&m->offset, f);
282 fputc('(', f);
283 if (req(m->base, R))
284 fprintf(f, "%%rip");
285 else
286 fprintf(f, "%%%s", regtoa(m->base.val, SLong));
287 if (!req(m->index, R))
288 fprintf(f, ", %%%s, %d",
289 regtoa(m->index.val, SLong),
290 m->scale
292 fputc(')', f);
293 break;
294 case RCon:
295 fputc('$', f);
296 emitcon(&fn->con[ref.val], f);
297 break;
298 default:
299 die("unreachable");
301 break;
302 case 'L':
303 sz = SLong;
304 goto Ref;
305 case 'W':
306 sz = SWord;
307 goto Ref;
308 case 'H':
309 sz = SShort;
310 goto Ref;
311 case 'B':
312 sz = SByte;
313 goto Ref;
314 case 'M':
315 c = *s++;
316 ref = getarg(c, i);
317 switch (rtype(ref)) {
318 case RMem:
319 goto Mem;
320 case RSlot:
321 fprintf(f, "%d(%%rbp)", slot(ref.val, fn));
322 break;
323 case RCon:
324 emitcon(&fn->con[ref.val], f);
325 fprintf(f, "(%%rip)");
326 break;
327 case RTmp:
328 assert(isreg(ref));
329 fprintf(f, "(%%%s)", regtoa(ref.val, SLong));
330 break;
331 default:
332 die("unreachable");
334 break;
335 default:
336 die("invalid format specifier %%%c", c);
338 goto Next;
341 static void
342 emitins(Ins i, Fn *fn, FILE *f)
344 Ref r;
345 int64_t val;
346 int o;
348 switch (i.op) {
349 default:
350 Table:
351 /* most instructions are just pulled out of
352 * the table omap[], some special cases are
353 * detailed below */
354 for (o=0;; o++) {
355 /* this linear search should really be a binary
356 * search */
357 if (omap[o].op == NOp)
358 die("no match for %s(%d)", opdesc[i.op].name, i.cls);
359 if (omap[o].op == i.op)
360 if (omap[o].cls == i.cls
361 || (omap[o].cls == Ki && KBASE(i.cls) == 0)
362 || (omap[o].cls == Ka))
363 break;
365 emitf(omap[o].asm, &i, fn, f);
366 break;
367 case ONop:
368 /* just do nothing for nops, they are inserted
369 * by some passes */
370 break;
371 case OMul:
372 /* here, we try to use the 3-addresss form
373 * of multiplication when possible */
374 if (rtype(i.arg[1]) == RCon) {
375 r = i.arg[0];
376 i.arg[0] = i.arg[1];
377 i.arg[1] = r;
379 if (KBASE(i.cls) == 0 /* only available for ints */
380 && rtype(i.arg[0]) == RCon
381 && rtype(i.arg[1]) == RTmp) {
382 emitf("imul%k %0, %1, %=", &i, fn, f);
383 break;
385 goto Table;
386 case OSub:
387 /* we have to use the negation trick to handle
388 * some 3-address substractions */
389 if (req(i.to, i.arg[1])) {
390 emitf("neg%k %=", &i, fn, f);
391 emitf("add%k %0, %=", &i, fn, f);
392 break;
394 goto Table;
395 case OCopy:
396 /* make sure we don't emit useless copies,
397 * also, we can use a trick to load 64-bits
398 * registers, it's detailed in my note below
399 * http://c9x.me/art/notes.html?09/19/2015 */
400 if (req(i.to, R) || req(i.arg[0], R))
401 break;
402 if (isreg(i.to)
403 && rtype(i.arg[0]) == RCon
404 && i.cls == Kl
405 && fn->con[i.arg[0].val].type == CBits
406 && (val = fn->con[i.arg[0].val].bits.i) >= 0
407 && val <= UINT32_MAX) {
408 emitf("movl %W0, %W=", &i, fn, f);
409 } else if (isreg(i.to)
410 && rtype(i.arg[0]) == RCon
411 && fn->con[i.arg[0].val].type == CAddr) {
412 emitf("lea%k %M0, %=", &i, fn, f);
413 } else if (!req(i.arg[0], i.to))
414 emitf("mov%k %0, %=", &i, fn, f);
415 break;
416 case OCall:
417 /* calls simply have a weird syntax in AT&T
418 * assembly... */
419 switch (rtype(i.arg[0])) {
420 case RCon:
421 fprintf(f, "\tcallq ");
422 emitcon(&fn->con[i.arg[0].val], f);
423 fprintf(f, "\n");
424 break;
425 case RTmp:
426 emitf("callq *%L0", &i, fn, f);
427 break;
428 default:
429 die("invalid call argument");
431 break;
432 case OSAlloc:
433 /* there is no good reason why this is here
434 * maybe we should split OSAlloc in 2 different
435 * instructions depending on the result
437 emitf("subq %L0, %%rsp", &i, fn, f);
438 if (!req(i.to, R))
439 emitcopy(i.to, TMP(RSP), Kl, fn, f);
440 break;
441 case OSwap:
442 if (KBASE(i.cls) == 0)
443 goto Table;
444 /* for floats, there is no swap instruction
445 * so we use xmm15 as a temporary
447 emitcopy(TMP(XMM0+15), i.arg[0], i.cls, fn, f);
448 emitcopy(i.arg[0], i.arg[1], i.cls, fn, f);
449 emitcopy(i.arg[1], TMP(XMM0+15), i.cls, fn, f);
450 break;
454 static int
455 cneg(int cmp)
457 switch (cmp) {
458 default: die("invalid int comparison %d", cmp);
459 case ICule: return ICugt;
460 case ICult: return ICuge;
461 case ICsle: return ICsgt;
462 case ICslt: return ICsge;
463 case ICsgt: return ICsle;
464 case ICsge: return ICslt;
465 case ICugt: return ICule;
466 case ICuge: return ICult;
467 case ICeq: return ICne;
468 case ICne: return ICeq;
469 case ICXnp: return ICXp;
470 case ICXp: return ICXnp;
474 static int
475 framesz(Fn *fn)
477 int i, o, f;
479 /* specific to NAlign == 3 */
480 for (i=0, o=0; i<NRClob; i++)
481 o ^= 1 & (fn->reg >> rclob[i]);
482 f = fn->slot;
483 f = (f + 3) & -4;
484 return 4*f + 8*o;
487 void
488 emitfn(Fn *fn, FILE *f)
490 static char *ctoa[] = {
491 [ICeq] = "z",
492 [ICule] = "be",
493 [ICult] = "b",
494 [ICsle] = "le",
495 [ICslt] = "l",
496 [ICsgt] = "g",
497 [ICsge] = "ge",
498 [ICugt] = "a",
499 [ICuge] = "ae",
500 [ICne] = "nz",
501 [ICXnp] = "np",
502 [ICXp] = "p"
504 static int id0;
505 Blk *b, *s;
506 Ins *i, itmp;
507 int *r, c, fs;
509 fprintf(f, ".text\n");
510 if (fn->export)
511 fprintf(f, ".globl %s%s\n", symprefix, fn->name);
512 fprintf(f,
513 "%s%s:\n"
514 "\tpush %%rbp\n"
515 "\tmov %%rsp, %%rbp\n",
516 symprefix, fn->name
518 fs = framesz(fn);
519 if (fs)
520 fprintf(f, "\tsub $%d, %%rsp\n", fs);
521 for (r=rclob; r-rclob < NRClob; r++)
522 if (fn->reg & BIT(*r)) {
523 itmp.arg[0] = TMP(*r);
524 emitf("pushq %L0", &itmp, fn, f);
527 for (b=fn->start; b; b=b->link) {
528 fprintf(f, "%sbb%d: /* %s */\n", locprefix, id0+b->id, b->name);
529 for (i=b->ins; i!=&b->ins[b->nins]; i++)
530 emitins(*i, fn, f);
531 switch (b->jmp.type) {
532 case JRet0:
533 for (r=&rclob[NRClob]; r>rclob;)
534 if (fn->reg & BIT(*--r)) {
535 itmp.arg[0] = TMP(*r);
536 emitf("popq %L0", &itmp, fn, f);
538 fprintf(f,
539 "\tleave\n"
540 "\tret\n"
542 break;
543 case JJmp:
544 Jmp:
545 if (b->s1 != b->link)
546 fprintf(f, "\tjmp %sbb%d /* %s */\n",
547 locprefix, id0+b->s1->id, b->s1->name);
548 break;
549 default:
550 c = b->jmp.type - JXJc;
551 if (0 <= c && c <= NXICmp) {
552 if (b->link == b->s2) {
553 s = b->s1;
554 b->s1 = b->s2;
555 b->s2 = s;
556 } else
557 c = cneg(c);
558 fprintf(f, "\tj%s %sbb%d /* %s */\n", ctoa[c],
559 locprefix, id0+b->s2->id, b->s2->name);
560 goto Jmp;
562 die("unhandled jump %d", b->jmp.type);
565 id0 += fn->nblk;
568 void
569 emitdat(Dat *d, FILE *f)
571 static int align;
572 static char *dtoa[] = {
573 [DAlign] = ".align",
574 [DB] = "\t.byte",
575 [DH] = "\t.value",
576 [DW] = "\t.long",
577 [DL] = "\t.quad"
580 switch (d->type) {
581 case DStart:
582 align = 0;
583 fprintf(f, ".data\n");
584 break;
585 case DEnd:
586 break;
587 case DName:
588 if (!align)
589 fprintf(f, ".align 8\n");
590 if (d->export)
591 fprintf(f, ".globl %s%s\n", symprefix, d->u.str);
592 fprintf(f, "%s%s:\n", symprefix, d->u.str);
593 break;
594 case DZ:
595 fprintf(f, "\t.fill %"PRId64",1,0\n", d->u.num);
596 break;
597 default:
598 if (d->type == DAlign)
599 align = 1;
601 if (d->isstr) {
602 if (d->type != DB)
603 err("strings only supported for 'b' currently");
604 fprintf(f, "\t.ascii \"%s\"\n", d->u.str);
606 else if (d->isref) {
607 fprintf(f, "%s %s%+"PRId64"\n",
608 dtoa[d->type], d->u.ref.nam,
609 d->u.ref.off);
611 else {
612 fprintf(f, "%s %"PRId64"\n",
613 dtoa[d->type], d->u.num);
615 break;
619 typedef struct FBits FBits;
621 struct FBits {
622 union {
623 int64_t n;
624 float f;
625 double d;
626 } bits;
627 int wide;
628 FBits *link;
631 static FBits *stash;
634 stashfp(int64_t n, int w)
636 FBits **pb, *b;
637 int i;
639 /* does a dumb de-dup of fp constants
640 * this should be the linker's job */
641 for (pb=&stash, i=0; (b=*pb); pb=&b->link, i++)
642 if (n == b->bits.n && w == b->wide)
643 return i;
644 b = emalloc(sizeof *b);
645 b->bits.n = n;
646 b->wide = w;
647 b->link = 0;
648 *pb = b;
649 return i;
652 void
653 emitfin(FILE *f)
655 FBits *b;
656 int i;
658 if (!stash)
659 return;
660 fprintf(f, "/* floating point constants */\n");
661 fprintf(f, ".data\n.align 8\n");
662 for (b=stash, i=0; b; b=b->link, i++)
663 if (b->wide)
664 fprintf(f,
665 "%sfp%d:\n"
666 "\t.quad %"PRId64
667 " /* %f */\n",
668 locprefix, i, b->bits.n,
669 b->bits.d
671 for (b=stash, i=0; b; b=b->link, i++)
672 if (!b->wide)
673 fprintf(f,
674 "%sfp%d:\n"
675 "\t.long %"PRId64
676 " /* %lf */\n",
677 locprefix, i, b->bits.n & 0xffffffff,
678 b->bits.f
680 while ((b=stash)) {
681 stash = b->link;
682 free(b);