ncc: handle + unary operator as in 1 + - + - 1
[neatcc.git] / arm.c
blob242d808edf6f024f9c703d5ec19cd6a686ee9ff9
1 #include <string.h>
2 #include "tok.h"
3 #include "gen.h"
4 #include "out.h"
6 #define MIN(a, b) ((a) < (b) ? (a) : (b))
7 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
8 #define oi4(i) oi((i), 4)
10 #define REG_DP 10 /* data pointer register */
11 #define REG_TMP 12 /* temporary register */
12 #define REG_LR 14 /* link register */
13 #define REG_PC 15 /* program counter */
15 #define I_AND 0x00
16 #define I_EOR 0x01
17 #define I_SUB 0x02
18 #define I_RSB 0x03
19 #define I_ADD 0x04
20 #define I_TST 0x08
21 #define I_CMP 0x0a
22 #define I_ORR 0x0c
23 #define I_MOV 0x0d
24 #define I_MVN 0x0f
26 int tmpregs[] = {4, 5, 6, 7, 8, 9, 3, 2, 1, 0};
27 int argregs[] = {0, 1, 2, 3};
29 /* compiled division functions; div.s contains the source */
30 static int udivdi3[] = {
31 0xe3a02000, 0xe3a03000, 0xe1110001, 0x0a00000a,
32 0xe1b0c211, 0xe2822001, 0x5afffffc, 0xe3a0c001,
33 0xe2522001, 0x4a000004, 0xe1500211, 0x3afffffb,
34 0xe0400211, 0xe083321c, 0xeafffff8, 0xe1a01000,
35 0xe1a00003, 0xe1a0f00e,
37 static int umoddi3[] = {
38 0xe92d4000, 0xebffffeb, 0xe1a00001, 0xe8bd8000,
40 static int divdi3[] = {
41 0xe92d4030, 0xe1a04000, 0xe1a05001, 0xe1100000,
42 0x42600000, 0xe1110001, 0x42611000, 0xebffffe1,
43 0xe1340005, 0x42600000, 0xe1140004, 0x42611000,
44 0xe8bd8030,
46 static int moddi3[] = {
47 0xe92d4000, 0xebfffff0, 0xe1a00001, 0xe8bd8000,
50 /* output div/mod functions */
51 static int putdiv = 0;
53 static void insert_spsub(void);
55 static void i_div(char *func)
57 putdiv = 1;
58 insert_spsub();
59 i_call(func, 0);
62 void i_done(void)
64 if (putdiv) {
65 out_sym("__udivdi3", OUT_CS, cslen, 0);
66 os(udivdi3, sizeof(udivdi3));
67 out_sym("__umoddi3", OUT_CS, cslen, 0);
68 os(umoddi3, sizeof(umoddi3));
69 out_sym("__divdi3", OUT_CS, cslen, 0);
70 os(divdi3, sizeof(divdi3));
71 out_sym("__moddi3", OUT_CS, cslen, 0);
72 os(moddi3, sizeof(moddi3));
76 /* for optimizing cmp + bcc */
77 #define OPT_ISCMP() (last_cmp + 12 == cslen && last_set + 4 == cslen)
78 #define OPT_CCOND() (*(unsigned int *) ((void *) cs + last_set) >> 28)
80 static long last_cmp = -1;
81 static long last_set = -1;
83 #define MAXNUMS 1024
85 /* data pool */
86 static long num_offs[MAXNUMS]; /* data immediate value */
87 static char num_names[MAXNUMS][NAMELEN]; /* relocation data symbol name */
88 static int nums;
90 static int pool_find(char *name, int off)
92 int i;
93 for (i = 0; i < nums; i++)
94 if (!strcmp(name, num_names[i]) && off == num_offs[i])
95 return i;
96 return -1;
99 static int pool_num(long num)
101 int idx = pool_find("", num);
102 if (idx < 0) {
103 idx = nums++;
104 num_offs[idx] = num;
105 num_names[idx][0] = '\0';
107 return idx << 2;
110 static int pool_reloc(char *name, long off)
112 int idx = pool_find(name, off);
113 if (idx < 0) {
114 idx = nums++;
115 num_offs[idx] = off;
116 strcpy(num_names[idx], name);
118 return idx << 2;
121 static void pool_write(void)
123 int i;
124 for (i = 0; i < nums; i++) {
125 if (num_names[i] && !pass1)
126 out_rel(num_names[i], OUT_CS, cslen);
127 oi4(num_offs[i]);
132 * data processing:
133 * +---------------------------------------+
134 * |COND|00|I| op |S| Rn | Rd | operand2 |
135 * +---------------------------------------+
137 * S: set condition code
138 * Rn: first operand
139 * Rd: destination operand
141 * I=0 operand2=| shift | Rm |
142 * I=1 operand2=|rota| imm |
144 #define ADD(op, rd, rn, s, i, cond) \
145 (((cond) << 28) | ((i) << 25) | ((s) << 20) | \
146 ((op) << 21) | ((rn) << 16) | ((rd) << 12))
148 static int add_encimm(unsigned n)
150 int i = 0;
151 while (i < 12 && (n >> ((4 + i) << 1)))
152 i++;
153 return (n >> (i << 1)) | (((16 - i) & 0x0f) << 8);
156 static unsigned add_decimm(int n)
158 int rot = (16 - ((n >> 8) & 0x0f)) & 0x0f;
159 return (n & 0xff) << (rot << 1);
162 static int add_rndimm(unsigned n)
164 int rot = (n >> 8) & 0x0f;
165 int num = n & 0xff;
166 if (rot == 0)
167 return n;
168 if (num == 0xff) {
169 num = 0;
170 rot = (rot + 12) & 0x0f;
172 return ((num + 1) & 0xff) | (rot << 8);
175 static int opcode_add(int op)
177 /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */
178 static int rx[] = {I_ADD, I_SUB, I_AND, I_ORR, I_EOR};
179 return rx[op & 0x0f];
182 static void i_add(int op, int rd, int rn, int rm)
184 oi4(ADD(opcode_add(op), rd, rn, 0, 0, 14) | rm);
187 int i_imm(int op, long imm)
189 return (op & 0xf0) != 0x20 && add_decimm(add_encimm(imm)) == imm;
192 static void i_add_imm(int op, int rd, int rn, long n)
194 oi4(ADD(opcode_add(op), rd, rn, 0, 1, 14) | add_encimm(n));
197 static void i_ldr(int l, int rd, int rn, int off, int bt);
199 void i_num(int rd, long n)
201 int enc = add_encimm(n);
202 if (n == add_decimm(enc)) {
203 oi4(ADD(I_MOV, rd, 0, 0, 1, 14) | enc);
204 return;
206 enc = add_encimm(-n - 1);
207 if (~n == add_decimm(enc)) {
208 oi4(ADD(I_MVN, rd, 0, 0, 1, 14) | enc);
209 return;
211 i_ldr(1, rd, REG_DP, pool_num(n), LONGSZ);
214 static void i_add_anyimm(int rd, int rn, long n)
216 int neg = n < 0;
217 int imm = add_encimm(neg ? -n : n);
218 if (imm == add_decimm(neg ? -n : n)) {
219 oi4(ADD(neg ? I_SUB : I_ADD, rd, rn, 0, 1, 14) | imm);
220 } else {
221 i_num(rd, n);
222 i_add(O_ADD, rd, rd, rn);
227 * multiply
228 * +----------------------------------------+
229 * |COND|000000|A|S| Rd | Rn | Rs |1001| Rm |
230 * +----------------------------------------+
232 * Rd: destination
233 * A: accumulate
234 * C: set condition codes
236 * I=0 operand2=| shift | Rm |
237 * I=1 operand2=|rota| imm |
239 #define MUL(rd, rn, rs) \
240 ((14 << 28) | ((rd) << 16) | ((0) << 12) | ((rn) << 8) | ((9) << 4) | (rm))
242 static void i_mul(int rd, int rn, int rm)
244 oi4(MUL(rd, rn, rm));
247 static int opcode_set(int op)
249 /* lt, gt, le, ge, eq, neq */
250 static int ucond[] = {3, 8, 9, 2, 0, 1};
251 static int scond[] = {11, 12, 13, 10, 0, 1};
252 return op & O_SIGNED ? scond[op & 0x0f] : ucond[op & 0x0f];
255 static void i_tst(int rn, int rm)
257 oi4(ADD(I_TST, 0, rn, 1, 0, 14) | rm);
260 static void i_cmp(int rn, int rm)
262 last_cmp = cslen;
263 oi4(ADD(I_CMP, 0, rn, 1, 0, 14) | rm);
266 static void i_cmp_imm(int rn, long n)
268 last_cmp = cslen;
269 oi4(ADD(I_CMP, 0, rn, 1, 1, 14) | add_encimm(n));
272 static void i_set(int cond, int rd)
274 oi4(ADD(I_MOV, rd, 0, 0, 1, 14));
275 last_set = cslen;
276 oi4(ADD(I_MOV, rd, 0, 0, 1, opcode_set(cond)) | 1);
279 #define SM_LSL 0
280 #define SM_LSR 1
281 #define SM_ASR 2
283 static int opcode_shl(int op)
285 if (op & 0x0f)
286 return op & O_SIGNED ? SM_ASR : SM_LSR;
287 return SM_LSL;
290 static void i_shl(int op, int rd, int rm, int rs)
292 int sm = opcode_shl(op);
293 oi4(ADD(I_MOV, rd, 0, 0, 0, 14) | (rs << 8) | (sm << 5) | (1 << 4) | rm);
296 static void i_shl_imm(int op, int rd, int rn, long n)
298 int sm = opcode_shl(op);
299 oi4(ADD(I_MOV, rd, 0, 0, 0, 14) | (n << 7) | (sm << 5) | rn);
302 void i_mov(int rd, int rn)
304 oi4(ADD(I_MOV, rd, 0, 0, 0, 14) | rn);
308 * single data transfer:
309 * +------------------------------------------+
310 * |COND|01|I|P|U|B|W|L| Rn | Rd | offset |
311 * +------------------------------------------+
313 * I: immediate/offset
314 * P: post/pre indexing
315 * U: down/up
316 * B: byte/word
317 * W: writeback
318 * L: store/load
319 * Rn: base register
320 * Rd: source/destination register
322 * I=0 offset=| immediate |
323 * I=1 offset=| shift | Rm |
325 * halfword and signed data transfer
326 * +----------------------------------------------+
327 * |COND|000|P|U|0|W|L| Rn | Rd |0000|1|S|H|1| Rm |
328 * +----------------------------------------------+
330 * +----------------------------------------------+
331 * |COND|000|P|U|1|W|L| Rn | Rd |off1|1|S|H|1|off2|
332 * +----------------------------------------------+
334 * S: singed
335 * H: halfword
337 #define LDR(l, rd, rn, b, u, p, w) \
338 ((14 << 28) | (1 << 26) | ((p) << 24) | ((b) << 22) | ((u) << 23) | \
339 ((w) << 21) | ((l) << 20) | ((rn) << 16) | ((rd) << 12))
340 #define LDRH(l, rd, rn, s, h, u, i) \
341 ((14 << 28) | (1 << 24) | ((u) << 23) | ((i) << 22) | ((l) << 20) | \
342 ((rn) << 16) | ((rd) << 12) | ((s) << 6) | ((h) << 5) | (9 << 4))
344 static void i_ldr(int l, int rd, int rn, int off, int bt)
346 int b = BT_SZ(bt) == 1;
347 int h = BT_SZ(bt) == 2;
348 int s = l && (bt & BT_SIGNED);
349 int half = h || (b && s);
350 int maximm = half ? 0x100 : 0x1000;
351 int neg = off < 0;
352 if (neg)
353 off = -off;
354 while (off >= maximm) {
355 int imm = add_encimm(off);
356 oi4(ADD(neg ? I_SUB : I_ADD, REG_TMP, rn, 0, 1, 14) | imm);
357 rn = REG_TMP;
358 off -= add_decimm(imm);
360 if (!half)
361 oi4(LDR(l, rd, rn, b, !neg, 1, 0) | off);
362 else
363 oi4(LDRH(l, rd, rn, s, h, !neg, 1) |
364 ((off & 0xf0) << 4) | (off & 0x0f));
367 void i_load(int rd, int rn, int off, int bt)
369 i_ldr(1, rd, rn, off, bt);
372 void i_save(int rd, int rn, int off, int bt)
374 i_ldr(0, rd, rn, off, bt);
377 void i_sym(int rd, char *sym, int off)
379 int doff = pool_reloc(sym, off);
380 i_ldr(1, rd, REG_DP, doff, LONGSZ);
383 static void i_neg(int rd, int r1)
385 oi4(ADD(I_RSB, rd, r1, 0, 1, 14));
388 static void i_not(int rd, int r1)
390 oi4(ADD(I_MVN, rd, 0, 0, 0, 14) | r1);
393 static int cond_nots[] = {1, 0, 3, 2, -1, -1, -1, -1, 9, 8, 11, 10, 13, 12, -1};
395 static void i_lnot(int rd, int r1)
397 if (OPT_ISCMP()) {
398 unsigned int *lset = (void *) cs + last_set;
399 int cond = cond_nots[OPT_CCOND()];
400 *lset = (*lset & 0x0fffffff) | (cond << 28);
401 return;
403 i_tst(r1, r1);
404 i_set(O_EQ, rd);
407 /* rd = rd & ((1 << bits) - 1) */
408 static void i_zx(int rd, int r1, int bits)
410 if (bits <= 8) {
411 oi4(ADD(I_AND, rd, r1, 0, 1, 14) | add_encimm((1 << bits) - 1));
412 } else {
413 i_shl_imm(O_SHL, rd, r1, 32 - bits);
414 i_shl_imm(O_SHR, rd, rd, 32 - bits);
418 static void i_sx(int rd, int r1, int bits)
420 i_shl_imm(O_SHL, rd, r1, 32 - bits);
421 i_shl_imm(O_SIGNED | O_SHR, rd, rd, 32 - bits);
425 * branch:
426 * +-----------------------------------+
427 * |COND|101|L| offset |
428 * +-----------------------------------+
430 * L: link
432 #define BL(cond, l, o) (((cond) << 28) | (5 << 25) | ((l) << 24) | \
433 ((((o) - 8) >> 2) & 0x00ffffff))
434 void i_jmp(int rn, int jc, int nbytes)
436 if (!nbytes)
437 return;
438 if (rn < 0) {
439 oi4(BL(14, 0, 0));
440 return;
442 if (OPT_ISCMP()) {
443 int cond = OPT_CCOND();
444 cslen = last_cmp + 4;
445 last_set = -1;
446 oi4(BL(jc ? cond_nots[cond] : cond, 0, 0));
447 return;
449 i_tst(rn, rn);
450 oi4(BL(jc ? 0 : 1, 0, 0));
453 long i_fill(long src, long dst, int nbytes)
455 long *d = (void *) cs + src - 4;
456 if (!nbytes)
457 return 0;
458 *d = (*d & 0xff000000) | (((dst - src - 4) >> 2) & 0x00ffffff);
459 return dst - src;
462 void i_memcpy(int rd, int rs, int rn)
464 oi4(ADD(I_SUB, rn, rn, 1, 1, 14) | 1);
465 oi4(BL(4, 0, 16));
466 oi4(LDR(1, REG_TMP, rs, 1, 1, 0, 0) | 1);
467 oi4(LDR(0, REG_TMP, rd, 1, 1, 0, 0) | 1);
468 oi4(BL(14, 0, -16));
471 void i_memset(int rd, int rs, int rn)
473 oi4(ADD(I_SUB, rn, rn, 1, 1, 14) | 1);
474 oi4(BL(4, 0, 12));
475 oi4(LDR(0, rs, rd, 1, 1, 0, 0) | 1);
476 oi4(BL(14, 0, -12));
479 void i_call_reg(int rd)
481 i_mov(REG_LR, REG_PC);
482 i_mov(REG_PC, rd);
485 void i_call(char *sym, int off)
487 if (!pass1)
488 out_rel(sym, OUT_CS | OUT_RLREL | OUT_RL24, cslen);
489 oi4(BL(14, 1, off));
492 void i_reg(int op, int *rd, int *r1, int *r2, int *tmp)
494 *rd = R_TMPS;
495 *r1 = R_TMPS;
496 *r2 = (op & O_IMM || (op & 0xf0) == 0x40) ? 0 : R_TMPS;
497 *tmp = 0;
498 if ((op & 0xff) == O_DIV || (op & 0xff) == O_MOD) {
499 *rd = 1 << REG_RET;
500 *r1 = 1 << argregs[0];
501 *r2 = 1 << argregs[1];
502 *tmp = R_TMPS & ~R_SAVED;
506 void i_op(int op, int rd, int r1, int r2)
508 if ((op & 0xf0) == 0x00)
509 i_add(op, rd, r1, r2);
510 if ((op & 0xf0) == 0x10)
511 i_shl(op, rd, r1, r2);
512 if ((op & 0xf0) == 0x20) {
513 if ((op & 0xff) == O_MUL)
514 i_mul(rd, r1, r2);
515 if ((op & 0xff) == O_DIV)
516 i_div(op & O_SIGNED ? "__divdi3" : "__udivdi3");
517 if ((op & 0xff) == O_MOD)
518 i_div(op & O_SIGNED ? "__moddi3" : "__umoddi3");
519 return;
521 if ((op & 0xf0) == 0x30) {
522 i_cmp(r1, r2);
523 i_set(op, rd);
524 return;
526 if ((op & 0xf0) == 0x40) { /* uop */
527 if ((op & 0xff) == O_NEG)
528 i_neg(rd, r1);
529 if ((op & 0xff) == O_NOT)
530 i_not(rd, r1);
531 if ((op & 0xff) == O_LNOT)
532 i_lnot(rd, r1);
533 return;
537 void i_op_imm(int op, int rd, int r1, long n)
539 if ((op & 0xf0) == 0x00) {
540 if (i_imm(O_ADD, n))
541 i_add_imm(op, rd, r1, n);
542 else
543 i_add_anyimm(rd, r1, n);
545 if ((op & 0xf0) == 0x10) /* shl */
546 i_shl_imm(op, rd, r1, n);
547 if ((op & 0xf0) == 0x30) { /* imm */
548 i_cmp_imm(r1, n);
549 i_set(op, rd);
551 if ((op & 0xf0) == 0x50) { /* etc */
552 if ((op & 0xff) == O_ZX)
553 i_zx(rd, r1, n);
554 if ((op & 0xff) == O_SX)
555 i_sx(rd, r1, n);
556 if ((op & 0xff) == O_MOV)
557 i_mov(rd, r1);
561 static int func_argc;
562 static int func_varg;
563 static int func_spsub;
564 static int func_sargs;
565 static int func_sregs;
566 static int func_initfp;
567 static int func_initdp = 1;
568 static int spsub_addr;
569 static int dpadd_addr;
571 static int saved_regs(int args)
573 int n = 2;
574 int i;
575 for (i = 0; i < N_REGS; i++) {
576 if ((1 << i) & func_sregs)
577 n++;
578 if (args && (1 << i) & func_sargs)
579 n++;
581 return n;
584 int i_args(void)
586 return saved_regs(0) * LONGSZ;
589 int i_sp(void)
591 return 0;
594 static int plain_function(void)
596 return !func_initfp && !func_spsub && !func_initdp && !func_varg &&
597 !func_sargs && !func_sregs && func_argc <= N_ARGS;
600 static void insert_spsub(void)
602 if (!func_spsub) {
603 func_spsub = 1;
604 spsub_addr = cslen;
605 oi4(0xe24dd000); /* sub sp, sp, xx */
609 void i_prolog(int argc, int varg, int sargs, int sregs, int initfp, int spsub)
611 last_set = -1;
612 nums = 0;
613 func_argc = argc;
614 func_varg = varg;
615 func_sargs = sargs;
616 func_sregs = sregs;
617 func_initfp = initfp;
618 func_spsub = 0;
619 if (plain_function())
620 return;
621 if (initfp)
622 func_sregs |= 1 << REG_FP;
623 if (func_initdp)
624 func_sregs |= 1 << REG_DP;
625 /* stack should remain 8-aligned */
626 if (saved_regs(1) & 0x1)
627 func_sregs |= 8;
628 oi4(0xe1a0c00d); /* mov r12, sp */
629 if (func_sargs)
630 oi4(0xe92d0000 | func_sargs); /* stmfd sp!, {r0-r3} */
631 oi4(0xe92d5000 | func_sregs); /* stmfd sp!, {r0-r11, r12, lr} */
632 if (func_initfp)
633 oi4(0xe1a0b00d); /* mov fp, sp */
634 if (spsub)
635 insert_spsub();
636 if (func_initdp) {
637 dpadd_addr = cslen;
638 oi4(0xe28fa000); /* add dp, pc, xx */
642 void i_epilog(int sp_max)
644 sp_max = -sp_max;
645 if (plain_function()) {
646 oi4(0xe1a0f00e); /* mov pc, lr */
647 return;
649 if (func_initfp)
650 oi4(0xe89ba000 | func_sregs);/* ldmfd fp, {r4-r11, sp, pc} */
651 if (!func_initfp)
652 oi4(0xe89da000 | func_sregs);/* ldmfd sp, {r4-r11, sp, pc} */
653 if (func_initdp) {
654 int dpoff = cslen - dpadd_addr - 8;
655 dpoff = add_decimm(add_rndimm(add_encimm(dpoff)));
656 cslen = dpadd_addr + dpoff + 8;
657 /* fill data ptr addition: dp = pc + xx */
658 *(long *) (cs + dpadd_addr) |= add_encimm(dpoff);
660 if (func_initfp && func_spsub) {
661 sp_max = ALIGN(sp_max, 8);
662 sp_max = add_decimm(add_rndimm(add_encimm(sp_max)));
663 /* fill stack sub: sp = sp - xx */
664 *(long *) (cs + spsub_addr) |= add_encimm(sp_max);
666 pool_write();