x86: use short jumps when possible
[neatcc.git] / x86.c
blob5283184b91d88f6e6c194011af5591a721e07ea7
1 /* architecture-dependent code generation for x86 */
2 #include <stdlib.h>
3 #include "ncc.h"
5 /* x86-64 registers, without r8-r15 */
6 #define R_RAX 0x00
7 #define R_RCX 0x01
8 #define R_RDX 0x02
9 #define R_RBX 0x03
10 #define R_RSP 0x04
11 #define R_RBP 0x05
12 #define R_RSI 0x06
13 #define R_RDI 0x07
15 #define REG_RET R_RAX
16 #define R_BYTE 0x0007
18 /* x86 opcodes */
19 #define I_MOV 0x89
20 #define I_MOVI 0xc7
21 #define I_MOVIR 0xb8
22 #define I_MOVR 0x8b
23 #define I_MOVSXD 0x63
24 #define I_SHX 0xd3
25 #define I_CMP 0x3b
26 #define I_TST 0x85
27 #define I_LEA 0x8d
28 #define I_NOT 0xf7
29 #define I_CALL 0xff
30 #define I_MUL 0xf7
31 #define I_XOR 0x33
32 #define I_CQO 0x99
33 #define I_PUSH 0x50
34 #define I_POP 0x58
36 #define MIN(a, b) ((a) < (b) ? (a) : (b))
37 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
39 int tmpregs[] = {0, 1, 2, 6, 7, 3};
40 int argregs[] = {0};
42 #define OP2(o2, o1) (0x010000 | ((o2) << 8) | (o1))
43 #define O2(op) (((op) >> 8) & 0xff)
44 #define O1(op) ((op) & 0xff)
45 #define MODRM(m, r1, r2) ((m) << 6 | (r1) << 3 | (r2))
47 static struct mem cs; /* generated code */
49 /* code generation functions */
50 static void os(void *s, int n)
52 mem_put(&cs, s, n);
55 static char *ointbuf(long n, int l)
57 static char buf[16];
58 int i;
59 for (i = 0; i < l; i++) {
60 buf[i] = n & 0xff;
61 n >>= 8;
63 return buf;
66 static void oi(long n, int l)
68 mem_put(&cs, ointbuf(n, l), l);
71 static void oi_at(long pos, long n, int l)
73 mem_cpy(&cs, pos, ointbuf(n, l), l);
76 static long opos(void)
78 return mem_len(&cs);
81 static void op_x(int op, int r1, int r2, int bt)
83 int sz = T_SZ(bt);
84 if (sz == 2)
85 oi(0x66, 1);
86 if (op & 0x10000)
87 oi(O2(op), 1);
88 oi(sz == 1 ? O1(op) & ~0x1 : O1(op), 1);
91 #define op_mr op_rm
93 /* op_*(): r=reg, m=mem, i=imm, s=sym */
94 static void op_rm(int op, int src, int base, int off, int bt)
96 int dis = off == (char) off ? 1 : 4;
97 int mod = dis == 4 ? 2 : 1;
98 if (!off && (base & 7) != R_RBP)
99 mod = 0;
100 op_x(op, src, base, bt);
101 oi(MODRM(mod, src & 0x07, base & 0x07), 1);
102 if ((base & 7) == R_RSP)
103 oi(0x24, 1);
104 if (mod)
105 oi(off, dis);
108 static void op_rr(int op, int src, int dst, int bt)
110 op_x(op, src, dst, bt);
111 oi(MODRM(3, src & 0x07, dst & 0x07), 1);
114 #define movrx_bt(bt) (LONGSZ)
116 static int movrx_op(int bt, int mov)
118 int sz = T_SZ(bt);
119 if (sz == 2)
120 return OP2(0x0f, bt & T_MSIGN ? 0xbf : 0xb7);
121 if (sz == 1)
122 return OP2(0x0f, bt & T_MSIGN ? 0xbe : 0xb6);
123 return mov;
126 static void mov_r2r(int rd, int r1, unsigned bt)
128 if (rd != r1 || T_SZ(bt) != LONGSZ)
129 op_rr(movrx_op(bt, I_MOVR), rd, r1, movrx_bt(bt));
132 static void i_mov(int rd, int rn)
134 op_rr(movrx_op(LONGSZ, I_MOVR), rd, rn, movrx_bt(LONGSZ));
137 static void i_add(int op, int rd, int r1, int r2)
139 /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */
140 static int rx[] = {0003, 0053, 0043, 0013, 0063};
141 op_rr(rx[op & 0x0f], rd, r2, LONGSZ);
144 static void i_add_imm(int op, int rd, int rn, long n)
146 /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */
147 static int rx[] = {0xc0, 0xe8, 0xe0, 0xc8, 0xf0};
148 unsigned char s[4] = {0x83, rx[op & 0x0f] | rd, n & 0xff};
149 os((void *) s, 3);
152 static void i_num(int rd, long n)
154 if (!n) {
155 op_rr(I_XOR, rd, rd, 4);
156 return;
157 } else {
158 op_x(I_MOVIR + (rd & 7), 0, rd, LONGSZ);
159 oi(n, LONGSZ);
163 static void i_mul(int rd, int r1, int r2)
165 if (r2 != R_RDX)
166 i_num(R_RDX, 0);
167 op_rr(I_MUL, 4, r2, LONGSZ);
170 static void i_div(int op, int rd, int r1, int r2)
172 long bt = O_T(op);
173 if (r2 != R_RDX) {
174 if (bt & T_MSIGN)
175 op_x(I_CQO, R_RAX, R_RDX, LONGSZ);
176 else
177 i_num(R_RDX, 0);
179 op_rr(I_MUL, bt & T_MSIGN ? 7 : 6, r2, LONGSZ);
182 static void i_tst(int rn, int rm)
184 op_rr(I_TST, rn, rm, LONGSZ);
187 static void i_cmp(int rn, int rm)
189 op_rr(I_CMP, rn, rm, LONGSZ);
192 static void i_cmp_imm(int rn, long n)
194 unsigned char s[4] = {0x83, 0xf8 | rn, n & 0xff};
195 os(s, 3);
198 static void i_shl(int op, int rd, int r1, int rs)
200 long bt = O_T(op);
201 int sm = 4;
202 if ((op & 0x0f) == 1)
203 sm = bt & T_MSIGN ? 7 : 5;
204 op_rr(I_SHX, sm, rd, LONGSZ);
207 static void i_shl_imm(int op, int rd, int rn, long n)
209 long bt = O_T(op);
210 int sm = (op & 0x1) ? (bt & T_MSIGN ? 0xf8 : 0xe8) : 0xe0;
211 char s[4] = {0xc1, sm | rn, n & 0xff};
212 os(s, 3);
215 static void i_neg(int rd)
217 op_rr(I_NOT, 3, rd, LONGSZ);
220 static void i_not(int rd)
222 op_rr(I_NOT, 2, rd, LONGSZ);
225 static int i_cond(long op)
227 /* lt, ge, eq, ne, le, gt */
228 static int ucond[] = {0x92, 0x93, 0x94, 0x95, 0x96, 0x97};
229 static int scond[] = {0x9c, 0x9d, 0x94, 0x95, 0x9e, 0x9f};
230 long bt = O_T(op);
231 return bt & T_MSIGN ? scond[op & 0x0f] : ucond[op & 0x0f];
234 static void i_set(long op, int rd)
236 char set[] = "\x0f\x00\xc0";
237 set[1] = i_cond(op);
238 os(set, 3); /* setl al */
239 os("\x0f\xb6\xc0", 3); /* movzx rax, al */
242 static void i_lnot(int rd)
244 char cmp[] = "\x83\xf8\x00";
245 cmp[1] |= rd;
246 os(cmp, 3); /* cmp rax, 0 */
247 i_set(O_EQ, rd);
250 static void jx(int x, int nbytes)
252 char op[2] = {0x0f};
253 if (nbytes == 1) {
254 oi(0x70 | (x & 0x0f), 1); /* jx $addr */
255 } else {
256 op[1] = x;
257 os(op, 2); /* jx $addr */
261 /* generate cmp or tst before a conditional jump */
262 static void i_jcmp(long op, long rn, long rm)
264 if (op & O_JZ)
265 i_tst(rn, rn);
266 if (op & O_JCC) {
267 if (op & O_NUM)
268 i_cmp_imm(rn, rm);
269 else
270 i_cmp(rn, rm);
274 /* generate a jump instruction and return the of its displacement */
275 static long i_jmp(long op, int nb)
277 if (op & O_JZ)
278 jx(O_C(op) == O_JZ ? 0x84 : 0x85, nb);
279 else if (op & O_JCC)
280 jx(i_cond(op) & ~0x10, nb);
281 else
282 os(nb == 1 ? "\xeb" : "\xe9", 1);
283 oi(0, nb);
284 return opos() - nb;
287 /* the length of a jump instruction opcode */
288 static int i_jlen(long op, int nb)
290 if (op & (O_JZ | O_JCC))
291 return nb ? 2 : 1;
292 return 1;
295 /* zero extend */
296 static void i_zx(int rd, int r1, int bits)
298 if (bits & 0x07) {
299 i_shl_imm(O_SHL, rd, rd, LONGSZ * 8 - bits);
300 i_shl_imm(O_SHR, rd, rd, LONGSZ * 8 - bits);
301 } else {
302 mov_r2r(rd, r1, bits >> 3);
306 /* sign extend */
307 static void i_sx(int rd, int r1, int bits)
309 mov_r2r(rd, r1, T_MSIGN | (bits >> 3));
312 static void i_cast(int rd, int rn, int bt)
314 if (T_SZ(bt) == 8) {
315 if (rd != rn)
316 i_mov(rd, rn);
317 } else {
318 if (bt & T_MSIGN)
319 i_sx(rd, rn, T_SZ(bt) * 8);
320 else
321 i_zx(rd, rn, T_SZ(bt) * 8);
325 static void i_add_anyimm(int rd, int rn, long n)
327 op_rm(I_LEA, rd, rn, n, LONGSZ);
330 static long *rel_sym; /* relocation symbols */
331 static long *rel_flg; /* relocation flags */
332 static long *rel_off; /* relocation offsets */
333 static long rel_n, rel_sz; /* relocation count */
335 static long lab_sz; /* label count */
336 static long *lab_loc; /* label offsets in cs */
337 static long jmp_n, jmp_sz; /* jump count */
338 static long *jmp_off; /* jump offsets */
339 static long *jmp_dst; /* jump destinations */
340 static long *jmp_op; /* jump opcode */
341 static long jmp_ret; /* the position of the last return jmp */
343 static void lab_add(long id)
345 while (id >= lab_sz) {
346 int lab_n = lab_sz;
347 lab_sz = MAX(128, lab_sz * 2);
348 lab_loc = mextend(lab_loc, lab_n, lab_sz, sizeof(*lab_loc));
350 lab_loc[id] = opos();
353 static void jmp_add(long op, long off, long dst)
355 if (jmp_n == jmp_sz) {
356 jmp_sz = MAX(128, jmp_sz * 2);
357 jmp_off = mextend(jmp_off, jmp_n, jmp_sz, sizeof(*jmp_off));
358 jmp_dst = mextend(jmp_dst, jmp_n, jmp_sz, sizeof(*jmp_dst));
359 jmp_op = mextend(jmp_op, jmp_n, jmp_sz, sizeof(*jmp_op));
361 jmp_off[jmp_n] = off;
362 jmp_dst[jmp_n] = dst;
363 jmp_op[jmp_n] = op;
364 jmp_n++;
367 void i_label(long id)
369 lab_add(id + 1);
372 static void i_rel(long sym, long flg, long off)
374 if (rel_n == rel_sz) {
375 rel_sz = MAX(128, rel_sz * 2);
376 rel_sym = mextend(rel_sym, rel_n, rel_sz, sizeof(*rel_sym));
377 rel_flg = mextend(rel_flg, rel_n, rel_sz, sizeof(*rel_flg));
378 rel_off = mextend(rel_off, rel_n, rel_sz, sizeof(*rel_off));
380 rel_sym[rel_n] = sym;
381 rel_flg[rel_n] = flg;
382 rel_off[rel_n] = off;
383 rel_n++;
386 static void i_sym(int rd, int sym, int off)
388 op_x(I_MOVIR + (rd & 7), 0, rd, LONGSZ);
389 i_rel(sym, OUT_CS, opos());
390 oi(off, LONGSZ);
393 static void i_saveregs(long sregs, long sregs_pos, int st)
395 int nsregs = 0;
396 int i;
397 for (i = 0; i < N_TMPS; i++)
398 if ((1 << tmpregs[i]) & sregs)
399 op_rm(st ? I_MOV : I_MOVR, tmpregs[i], REG_FP,
400 sregs_pos + nsregs++ * ULNG, ULNG);
403 void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, long sregs_pos)
405 long body_n;
406 void *body;
407 long diff; /* prologue length */
408 int i;
409 /* removing the last jmp to the epilogue */
410 if (jmp_ret + i_jlen(O_JMP, 4) + 4 == opos()) {
411 mem_cut(&cs, jmp_ret);
412 jmp_n--;
414 lab_add(0); /* the return label */
415 body_n = mem_len(&cs);
416 body = mem_get(&cs);
417 /* generating function prologue */
418 if (initfp) {
419 os("\x55", 1); /* push rbp */
420 os("\x89\xe5", 2); /* mov rbp, rsp */
422 if (spsub) {
423 os("\x81\xec", 2);
424 spsub = ALIGN(spsub, 8);
425 oi(spsub, 4);
427 i_saveregs(sregs, sregs_pos, 1); /* saving registers */
428 diff = mem_len(&cs);
429 mem_put(&cs, body, body_n);
430 free(body);
431 /* generating function epilogue */
432 i_saveregs(sregs, sregs_pos, 0); /* restoring saved registers */
433 if (initfp)
434 os("\xc9", 1); /* leave */
435 os("\xc3", 1); /* ret */
436 /* adjusting code offsets */
437 for (i = 0; i < rel_n; i++)
438 rel_off[i] += diff;
439 for (i = 0; i < jmp_n; i++)
440 jmp_off[i] += diff;
441 for (i = 0; i < lab_sz; i++)
442 lab_loc[i] += diff;
445 /* introduce shorter jumps, if possible */
446 static void i_shortjumps(int *nb)
448 long off = 0; /* current code offset */
449 long dif = 0; /* the difference after changing jump instructions */
450 int rel = 0; /* current relocation */
451 int lab = 1; /* current label */
452 long c_len = mem_len(&cs);
453 char *c = mem_get(&cs);
454 int i;
455 for (i = 0; i < jmp_n; i++)
456 nb[i] = abs(lab_loc[jmp_dst[i]] - jmp_off[i]) < 0x70 ? 1 : 4;
457 for (i = 0; i < jmp_n; i++) {
458 long cur = jmp_off[i] - i_jlen(jmp_op[i], 4);
459 while (rel < rel_n && rel_off[rel] <= cur)
460 rel_off[rel++] += dif;
461 while (lab < lab_sz && lab_loc[lab] <= cur)
462 lab_loc[lab++] += dif;
463 mem_put(&cs, c + off, cur - off);
464 jmp_off[i] = i_jmp(jmp_op[i], nb[i]);
465 off = cur + i_jlen(jmp_op[i], 4) + 4;
466 dif = mem_len(&cs) - off;
468 while (rel < rel_n)
469 rel_off[rel++] += dif;
470 while (lab < lab_sz)
471 lab_loc[lab++] += dif;
472 lab_loc[0] += dif;
473 mem_put(&cs, c + off, c_len - off);
474 free(c);
477 void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff, long *rcnt)
479 int *nb; /* number of bytes necessary for jump displacements */
480 int i;
481 /* more compact jmp instructions */
482 nb = malloc(jmp_n * sizeof(nb[0]));
483 for (i = 0; i < jmp_n; i++)
484 nb[i] = 4;
485 i_shortjumps(nb);
486 for (i = 0; i < jmp_n; i++) /* filling jmp destinations */
487 oi_at(jmp_off[i], lab_loc[jmp_dst[i]] -
488 jmp_off[i] - nb[i], nb[i]);
489 free(nb);
490 *c_len = mem_len(&cs);
491 *c = mem_get(&cs);
492 *rsym = rel_sym;
493 *rflg = rel_flg;
494 *roff = rel_off;
495 *rcnt = rel_n;
496 rel_sym = NULL;
497 rel_flg = NULL;
498 rel_off = NULL;
499 rel_n = 0;
500 rel_sz = 0;
501 jmp_n = 0;
504 void i_done(void)
506 free(jmp_off);
507 free(jmp_dst);
508 free(jmp_op);
509 free(lab_loc);
512 long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *tmp)
514 long oc = O_C(op);
515 long bt = O_T(op);
516 *rd = 0;
517 *r1 = 0;
518 *r2 = 0;
519 *r3 = 0;
520 *tmp = 0;
521 if (oc & O_MOV) {
522 *rd = R_TMPS;
523 if (oc & (O_NUM | O_SYM))
524 *r1 = oc & (O_NUM | O_SYM) ? LONGSZ * 8 : R_TMPS;
525 else
526 *r1 = T_SZ(bt) == 1 ? R_BYTE : R_TMPS;
527 return 0;
529 if (oc & O_ADD) {
530 *r1 = R_TMPS;
531 *r2 = oc & O_NUM ? (oc == O_ADD ? 32 : 8) : R_TMPS;
532 return 0;
534 if (oc & O_SHL) {
535 if (oc & O_NUM) {
536 *r1 = R_TMPS;
537 *r2 = 8;
538 } else {
539 *r2 = 1 << R_RCX;
540 *r1 = R_TMPS & ~*r2;
542 return 0;
544 if (oc & O_MUL) {
545 if (oc & O_NUM)
546 return 1;
547 *rd = oc == O_MOD ? (1 << R_RDX) : (1 << R_RAX);
548 *r1 = (1 << R_RAX);
549 *r2 = R_TMPS & ~*rd & ~*r1;
550 if (oc == O_DIV)
551 *r2 &= ~(1 << R_RDX);
552 *tmp = (1 << R_RDX) | (1 << R_RAX);
553 return 0;
555 if (oc & O_CMP) {
556 *rd = 1 << R_RAX;
557 *r1 = R_TMPS;
558 *r2 = oc & O_NUM ? 8 : R_TMPS;
559 return 0;
561 if (oc & O_UOP) {
562 if (oc == O_LNOT)
563 *r1 = 1 << R_RAX;
564 else
565 *r1 = R_TMPS;
566 return 0;
568 if (oc == O_MSET) {
569 *r1 = 1 << R_RDI;
570 *r2 = 1 << R_RAX;
571 *r3 = 1 << R_RCX;
572 *tmp = (1 << R_RDI) | (1 << R_RCX);
573 return 0;
575 if (oc == O_MCPY) {
576 *r1 = 1 << R_RDI;
577 *r2 = 1 << R_RSI;
578 *r3 = 1 << R_RCX;
579 *tmp = (1 << R_RDI) | (1 << R_RSI) | (1 << R_RCX);
580 return 0;
582 if (oc == O_RET) {
583 *r1 = (1 << REG_RET);
584 return 0;
586 if (oc & O_CALL) {
587 *rd = (1 << REG_RET);
588 *r1 = oc & O_SYM ? 0 : R_TMPS;
589 *tmp = R_TMPS & ~R_PERM;
590 return 0;
592 if (oc & O_LD) {
593 *rd = T_SZ(bt) == 1 ? R_BYTE : R_TMPS;
594 *r1 = R_TMPS;
595 *r2 = oc & O_NUM ? 0 : R_TMPS;
596 return 0;
598 if (oc & O_ST) {
599 *r1 = T_SZ(bt) == 1 ? R_BYTE : R_TMPS;
600 *r2 = R_TMPS;
601 *r3 = oc & O_NUM ? 0 : R_TMPS;
602 return 0;
604 if (oc & O_JZ) {
605 *r1 = R_TMPS;
606 return 0;
608 if (oc & O_JCC) {
609 *r1 = R_TMPS;
610 *r2 = oc & O_NUM ? 8 : R_TMPS;
611 return 0;
613 if (oc == O_JMP)
614 return 0;
615 return 1;
618 int i_imm(long lim, long n)
620 long max = (1 << (lim - 1)) - 1;
621 return n <= max && n + 1 >= -max;
624 long i_ins(long op, long rd, long r1, long r2, long r3)
626 long oc = O_C(op);
627 long bt = O_T(op);
628 if (oc & O_ADD) {
629 if (oc & O_NUM) {
630 if (rd == r1 && r2 <= 127 && r2 >= -128)
631 i_add_imm(op, r1, r1, r2);
632 else
633 i_add_anyimm(rd, r1, r2);
634 } else {
635 i_add(op, r1, r1, r2);
638 if (oc & O_SHL) {
639 if (oc & O_NUM)
640 i_shl_imm(op, r1, r1, r2);
641 else
642 i_shl(op, r1, r1, r2);
644 if (oc & O_MUL) {
645 if (oc == O_MUL)
646 i_mul(R_RAX, r1, r2);
647 if (oc == O_DIV)
648 i_div(op, R_RAX, r1, r2);
649 if (oc == O_MOD)
650 i_div(op, R_RDX, r1, r2);
651 return 0;
653 if (oc & O_CMP) {
654 if (oc & O_NUM)
655 i_cmp_imm(r1, r2);
656 else
657 i_cmp(r1, r2);
658 i_set(op, rd);
659 return 0;
661 if (oc & O_UOP) { /* uop */
662 if (oc == O_NEG)
663 i_neg(r1);
664 if (oc == O_NOT)
665 i_not(r1);
666 if (oc == O_LNOT)
667 i_lnot(r1);
668 return 0;
670 if (oc == O_CALL) {
671 op_rr(I_CALL, 2, r1, LONGSZ);
672 return 0;
674 if (oc == (O_CALL | O_SYM)) {
675 os("\xe8", 1); /* call $x */
676 i_rel(r1, OUT_CS | OUT_RLREL, opos());
677 oi(-4, 4);
678 return 0;
680 if (oc == (O_MOV | O_SYM)) {
681 i_sym(rd, r1, r2);
682 return 0;
684 if (oc == (O_MOV | O_NUM)) {
685 i_num(rd, r1);
686 return 0;
688 if (oc == O_MSET) {
689 os("\xfc\xf3\xaa", 3); /* cld; rep stosb */
690 return 0;
692 if (oc == O_MCPY) {
693 os("\xfc\xf3\xa4", 3); /* cld; rep movs */
694 return 0;
696 if (oc == O_RET) {
697 jmp_ret = opos();
698 jmp_add(O_JMP, i_jmp(op, 4), 0);
699 return 0;
701 if (oc == (O_LD | O_NUM)) {
702 op_rm(movrx_op(bt, I_MOVR), rd, r1, r2, movrx_bt(bt));
703 return 0;
705 if (oc == (O_ST | O_NUM)) {
706 op_rm(I_MOV, r1, r2, r3, bt);
707 return 0;
709 if (oc == O_MOV) {
710 i_cast(rd, r1, bt);
711 return 0;
713 if (oc & O_JXX) {
714 i_jcmp(op, r1, r2);
715 jmp_add(op, i_jmp(op, 4), r3 + 1);
716 return 0;
718 return 1;