gen: more compact version of memcpy() and memset()
[neatcc.git] / gen.c
bloba8c57118636b024499bd6775ed9dc63352ec9e91
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include "gen.h"
5 #include "out.h"
6 #include "tok.h"
8 #define LOC_REG 0x01
9 #define LOC_MEM 0x02
10 #define LOC_NUM 0x04
11 #define LOC_SYM 0x08
12 #define LOC_LOCAL 0x10
14 #define NREGS 16
16 #define REG_PC 15 /* program counter */
17 #define REG_LR 14 /* link register */
18 #define REG_SP 13 /* stack pointer */
19 #define REG_TMP 12 /* temporary register */
20 #define REG_FP 11 /* frame pointer register */
21 #define REG_DP 10 /* data pointer register */
22 #define REG_RET 0 /* returned value register */
24 #define MIN(a, b) ((a) < (b) ? (a) : (b))
25 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
27 #define TMP_REG(t) ((t)->loc == LOC_REG ? (t)->addr : reg_get(~0))
28 #define TMP_REG2(t, r) ((t)->loc == LOC_REG && (t)->addr != (r) ? \
29 (t)->addr : reg_get(~(1 << (r))))
30 #define TMP_REG3(t, r1, r2) ((t)->loc == LOC_REG && (t)->addr != (r1) && (t)->addr != (r2) ? \
31 (t)->addr : reg_get(~((1 << (r1)) | (1 << (r2)))))
33 static char cs[SECSIZE]; /* code segment */
34 static int cslen;
35 static char ds[SECSIZE]; /* data segment */
36 static int dslen;
37 static long bsslen; /* bss segment size */
39 static int nogen;
40 static long sp;
41 static long func_beg;
42 static long maxsp;
44 #define TMP(i) (&tmps[ntmp - 1 - (i)])
46 static struct tmp {
47 long addr;
48 char sym[NAMELEN];
49 long off; /* offset from a symbol or a local */
50 unsigned loc; /* variable location */
51 unsigned bt; /* type of address; zero when not a pointer */
52 } tmps[MAXTMP];
53 static int ntmp;
55 static int tmpsp;
57 static struct tmp *regs[NREGS];
58 static int tmpregs[] = {4, 5, 6, 7, 8, 9, 0, 1, 2, 3};
59 static int argregs[] = {0, 1, 2, 3};
61 #define I_AND 0x00
62 #define I_EOR 0x01
63 #define I_SUB 0x02
64 #define I_RSB 0x03
65 #define I_ADD 0x04
66 #define I_TST 0x08
67 #define I_CMP 0x0a
68 #define I_ORR 0x0c
69 #define I_MOV 0x0d
70 #define I_MVN 0x0f
72 #define MAXRET (1 << 8)
74 static long ret[MAXRET];
75 static int nret;
77 /* for optimizing cmp + bcc */
78 static long last_cmp = -1;
79 static long last_set = -1;
80 static long last_cond;
82 static void oi(long n)
84 if (nogen)
85 return;
86 *(int *) (cs + cslen) = n;
87 cslen += 4;
90 #define NDATS 1024
92 /* data pool */
93 static long num_offs[NDATS]; /* data immediate value */
94 static char num_names[NDATS][NAMELEN]; /* relocation data symbol name */
95 static int ndats;
97 static int pool_num(long num)
99 int idx = ndats++;
100 num_offs[idx] = num;
101 num_names[idx][0] = '\0';
102 return idx << 2;
105 static int pool_reloc(char *name, long off)
107 int idx = ndats++;
108 num_offs[idx] = off;
109 strcpy(num_names[idx], name);
110 return idx << 2;
113 static void pool_write(void)
115 int i;
116 for (i = 0; i < ndats; i++) {
117 if (num_names[i])
118 out_rel(num_names[i], OUT_CS, cslen);
119 oi(num_offs[i]);
124 * data processing:
125 * +---------------------------------------+
126 * |COND|00|I| op |S| Rn | Rd | operand2 |
127 * +---------------------------------------+
129 * S: set condition code
130 * Rn: first operand
131 * Rd: destination operand
133 * I=0 operand2=| shift | Rm |
134 * I=1 operand2=|rota| imm |
136 #define ADD(op, rd, rn, s, i, cond) \
137 (((cond) << 28) | ((i) << 25) | ((s) << 20) | \
138 ((op) << 21) | ((rn) << 16) | ((rd) << 12))
140 static void i_add(int op, int rd, int rn, int rm)
142 oi(ADD(op, rd, rn, 0, 0, 14) | rm);
145 static int add_encimm(unsigned n)
147 int i = 0;
148 while (i < 12 && (n >> ((4 + i) << 1)))
149 i++;
150 return (n >> (i << 1)) | (((16 - i) & 0x0f) << 8);
153 static int add_decimm(unsigned n)
155 int rot = (16 - ((n >> 8) & 0x0f)) & 0x0f;
156 return (n & 0xff) << (rot << 1);
159 static int add_rndimm(unsigned n)
161 int rot = (n >> 8) & 0x0f;
162 int num = n & 0xff;
163 if (rot == 0)
164 return n;
165 if (num == 0xff) {
166 num = 0;
167 rot = (rot + 12) & 0x0f;
169 return ((num + 1) & 0xff) | (rot << 8);
172 static void i_ldr(int l, int rd, int rn, int off, int bt);
174 static void i_num(int rd, long n)
176 int neg = n < 0;
177 int p = neg ? -n - 1 : n;
178 int enc = add_encimm(p);
179 if (p == add_decimm(enc)) {
180 oi(ADD(neg ? I_MVN : I_MOV, rd, 0, 0, 1, 14) | enc);
181 } else {
182 if (!nogen) {
183 int off = pool_num(n);
184 i_ldr(1, rd, REG_DP, off, LONGSZ);
189 static void i_add_imm(int op, int rd, int rn, int n)
191 int neg = n < 0;
192 int imm = add_encimm(neg ? -n : n);
193 if (imm == add_decimm(neg ? -n : n)) {
194 oi(ADD(neg ? I_SUB : I_ADD, rd, rn, 0, 1, 14) | imm);
195 } else {
196 i_num(rd, n);
197 i_add(I_ADD, rd, rd, rn);
202 * multiply
203 * +----------------------------------------+
204 * |COND|000000|A|S| Rd | Rn | Rs |1001| Rm |
205 * +----------------------------------------+
207 * Rd: destination
208 * A: accumulate
209 * C: set condition codes
211 * I=0 operand2=| shift | Rm |
212 * I=1 operand2=|rota| imm |
214 #define MUL(rd, rn, rs) \
215 ((14 << 28) | ((rd) << 16) | ((0) << 12) | ((rn) << 8) | ((9) << 4) | (rm))
217 static void i_mul(int rd, int rn, int rm)
219 oi(MUL(rd, rn, rm));
222 static void i_cmp(int op, int rn, int rm)
224 oi(ADD(op, 0, rn, 1, 0, 14) | rm);
225 last_cmp = cslen;
228 static void i_set(int cond, int rd)
230 last_set = cslen;
231 last_cond = cond;
232 oi(ADD(I_MOV, rd, 0, 0, 1, 14));
233 oi(ADD(I_MOV, rd, 0, 0, 1, cond) | 1);
236 #define SM_LSL 0
237 #define SM_LSR 1
238 #define SM_ASR 2
240 static void i_shl(int sm, int rd, int rm, int rs)
242 oi(ADD(I_MOV, rd, 0, 0, 0, 14) | (rs << 8) | (sm << 5) | (1 << 4) | rm);
245 static void i_shl_imm(int sm, int rd, int n)
247 oi(ADD(I_MOV, rd, 0, 0, 0, 14) | (n << 7) | (sm << 5) | rd);
250 static void i_mov(int op, int rd, int rn)
252 oi(ADD(op, rd, 0, 0, 0, 14) | rn);
256 * single data transfer:
257 * +------------------------------------------+
258 * |COND|01|I|P|U|B|W|L| Rn | Rd | offset |
259 * +------------------------------------------+
261 * I: immediate/offset
262 * P: post/pre indexing
263 * U: down/up
264 * B: byte/word
265 * W: writeback
266 * L: store/load
267 * Rn: base register
268 * Rd: source/destination register
270 * I=0 offset=| immediate |
271 * I=1 offset=| shift | Rm |
273 * halfword and signed data transfer
274 * +----------------------------------------------+
275 * |COND|000|P|U|0|W|L| Rn | Rd |0000|1|S|H|1| Rm |
276 * +----------------------------------------------+
278 * +----------------------------------------------+
279 * |COND|000|P|U|1|W|L| Rn | Rd |off1|1|S|H|1|off2|
280 * +----------------------------------------------+
282 * S: singed
283 * H: halfword
285 #define LDR(l, rd, rn, b, u, p, w) \
286 ((14 << 28) | (1 << 26) | ((p) << 24) | ((b) << 22) | ((u) << 23) | \
287 ((w) << 21) | ((l) << 20) | ((rn) << 16) | ((rd) << 12))
288 #define LDRH(l, rd, rn, s, h, u, i) \
289 ((14 << 28) | (1 << 24) | ((u) << 23) | ((i) << 22) | ((l) << 20) | \
290 ((rn) << 16) | ((rd) << 12) | ((s) << 6) | ((h) << 5) | (9 << 4))
292 static void i_ldr(int l, int rd, int rn, int off, int bt)
294 int b = BT_SZ(bt) == 1;
295 int h = BT_SZ(bt) == 2;
296 int s = l && (bt & BT_SIGNED);
297 int half = h || (b && s);
298 int maximm = half ? 0x100 : 0x1000;
299 int neg = off < 0;
300 if (neg)
301 off = -off;
302 while (off >= maximm) {
303 int imm = add_encimm(off);
304 oi(ADD(neg ? I_SUB : I_ADD, rd, rn, 0, 1, 14) | imm);
305 rn = rd;
306 off -= add_decimm(imm);
308 if (!half)
309 oi(LDR(l, rd, rn, b, !neg, 1, 0) | off);
310 else
311 oi(LDRH(l, rd, rn, s, h, !neg, 1) |
312 ((off & 0xf0) << 4) | (off & 0x0f));
315 static void i_sym(int rd, char *sym, int off)
317 if (!nogen) {
318 int doff = pool_reloc(sym, off);
319 i_ldr(1, rd, REG_DP, doff, LONGSZ);
323 static void i_neg(int rd)
325 oi(ADD(I_RSB, rd, rd, 0, 1, 14));
328 static void i_not(int rd)
330 oi(ADD(I_MVN, rd, 0, rd, 1, 14));
333 static void i_lnot(int rd)
335 i_cmp(I_TST, rd, rd);
336 oi(ADD(I_MOV, rd, 0, 0, 1, 14));
337 oi(ADD(I_MOV, rd, 0, 0, 1, 0) | 1);
340 /* rd = rd & ((1 << bits) - 1) */
341 static void i_zx(int rd, int bits)
343 if (bits <= 8) {
344 oi(ADD(I_AND, rd, rd, 0, 1, 14) | add_encimm((1 << bits) - 1));
345 } else {
346 i_shl_imm(SM_LSL, rd, 32 - bits);
347 i_shl_imm(SM_LSR, rd, 32 - bits);
351 static void i_sx(int rd, int bits)
353 i_shl_imm(SM_LSL, rd, 32 - bits);
354 i_shl_imm(SM_ASR, rd, 32 - bits);
358 * branch:
359 * +-----------------------------------+
360 * |COND|101|L| offset |
361 * +-----------------------------------+
363 * L: link
365 #define BL(cond, l, o) (((cond) << 28) | (5 << 25) | ((l) << 24) | \
366 ((((o) - 8) >> 2) & 0x00ffffff))
368 static void i_b(long addr)
370 oi(BL(14, 0, addr - cslen));
373 static void i_b_if(long addr, int rn, int z)
375 static int nots[] = {1, 0, -1, -1, -1, -1, -1, -1, -1, -1, 11, 10, 13, 12, -1};
376 if (last_cmp + 8 != cslen || last_set != last_cmp) {
377 i_cmp(I_TST, rn, rn);
378 oi(BL(z ? 0 : 1, 0, addr - cslen));
379 return;
381 cslen = last_cmp;
382 oi(BL(z ? nots[last_cond] : last_cond, 0, addr - cslen));
385 static void i_b_fill(long *dst, int diff)
387 *dst = (*dst & 0xff000000) | (((diff - 8) >> 2) & 0x00ffffff);
390 static void i_memcpy(int rd, int rs, int rn)
392 oi(ADD(I_TST, 0, rn, 1, 0, 14) | rn);
393 oi(BL(0, 0, 20));
394 oi(LDR(1, REG_TMP, rs, 1, 1, 0, 0) | 1);
395 oi(LDR(0, REG_TMP, rd, 1, 1, 0, 0) | 1);
396 oi(ADD(I_SUB, rn, rn, 0, 1, 14) | 1);
397 oi(BL(14, 0, -20));
400 static void i_memset(int rd, int rs, int rn)
402 oi(ADD(I_TST, 0, rn, 1, 0, 14) | rn);
403 oi(BL(0, 0, 16));
404 oi(LDR(0, rs, rd, 1, 1, 0, 0) | 1);
405 oi(ADD(I_SUB, rn, rn, 0, 1, 14) | 1);
406 oi(BL(14, 0, -16));
409 static void i_call_reg(int rd)
411 i_mov(I_MOV, REG_LR, REG_PC);
412 i_mov(I_MOV, REG_PC, rd);
415 static void i_call(char *sym)
417 if (!nogen)
418 out_rel(sym, OUT_CS | OUT_REL24, cslen);
419 oi(BL(14, 1, 0));
422 static void i_prolog(void)
424 func_beg = cslen;
425 ndats = 0;
426 oi(0xe1a0c00d); /* mov r12, sp */
427 oi(0xe92d000f); /* stmfd sp!, {r0-r3} */
428 oi(0xe92d5ff0); /* stmfd sp!, {r0-r11, r12, lr} */
429 oi(0xe1a0b00d); /* mov fp, sp */
430 oi(0xe24dd000); /* sub sp, sp, xx */
431 oi(0xe28fa000); /* add dp, pc, xx */
434 static void i_epilog(void)
436 int dpoff;
437 oi(0xe89baff0); /* ldmfd fp, {r4-r11, sp, pc} */
438 dpoff = add_decimm(add_rndimm(add_encimm(cslen - func_beg - 28)));
439 cslen = func_beg + dpoff + 28;
440 maxsp = ALIGN(maxsp, 8);
441 maxsp = add_decimm(add_rndimm(add_encimm(maxsp)));
442 /* fill stack sub: sp = sp - xx */
443 *(long *) (cs + func_beg + 16) |= add_encimm(maxsp);
444 /* fill data ptr addition: dp = pc + xx */
445 *(long *) (cs + func_beg + 20) |= add_encimm(dpoff);
446 pool_write();
449 static long sp_push(int size)
451 sp += size;
452 if (sp > maxsp)
453 maxsp = sp;
454 return sp;
457 static void tmp_mem(struct tmp *tmp)
459 int src = tmp->addr;
460 if (!(tmp->loc == LOC_REG))
461 return;
462 if (tmpsp == -1)
463 tmpsp = sp;
464 tmp->addr = -sp_push(LONGSZ);
465 i_ldr(0, src, REG_FP, tmp->addr, LONGSZ);
466 regs[src] = NULL;
467 tmp->loc = LOC_MEM;
470 static void num_cast(struct tmp *t, unsigned bt)
472 if (!(bt & BT_SIGNED) && BT_SZ(bt) != LONGSZ)
473 t->addr &= ((1l << (long) (BT_SZ(bt) * 8)) - 1);
474 if (bt & BT_SIGNED && BT_SZ(bt) != LONGSZ &&
475 t->addr > (1l << (BT_SZ(bt) * 8 - 1)))
476 t->addr = -((1l << (BT_SZ(bt) * 8)) - t->addr);
479 static void tmp_reg(struct tmp *tmp, int dst, int deref)
481 int bt = tmp->bt;
482 if (!tmp->bt)
483 deref = 0;
484 if (deref)
485 tmp->bt = 0;
486 if (tmp->loc == LOC_NUM) {
487 i_num(dst, tmp->addr);
488 tmp->addr = dst;
489 regs[dst] = tmp;
490 tmp->loc = LOC_REG;
492 if (tmp->loc == LOC_SYM) {
493 i_sym(dst, tmp->sym, tmp->off);
494 tmp->addr = dst;
495 regs[dst] = tmp;
496 tmp->loc = LOC_REG;
498 if (tmp->loc == LOC_REG) {
499 if (deref)
500 i_ldr(1, dst, tmp->addr, 0, bt);
501 else if (dst != tmp->addr)
502 i_mov(I_MOV, dst, tmp->addr);
503 regs[tmp->addr] = NULL;
505 if (tmp->loc == LOC_LOCAL) {
506 if (deref)
507 i_ldr(1, dst, REG_FP, tmp->addr + tmp->off, bt);
508 else
509 i_add_imm(I_ADD, dst, REG_FP, tmp->addr + tmp->off);
511 if (tmp->loc == LOC_MEM) {
512 i_ldr(1, dst, REG_FP, tmp->addr, LONGSZ);
513 if (deref)
514 i_ldr(1, dst, dst, 0, bt);
516 tmp->addr = dst;
517 regs[dst] = tmp;
518 tmp->loc = LOC_REG;
521 static void reg_free(int reg)
523 int i;
524 if (!regs[reg])
525 return;
526 for (i = 0; i < ARRAY_SIZE(tmpregs); i++)
527 if (!regs[tmpregs[i]]) {
528 tmp_reg(regs[reg], tmpregs[i], 0);
529 return;
531 tmp_mem(regs[reg]);
534 static void reg_for(int reg, struct tmp *t)
536 if (regs[reg] && regs[reg] != t)
537 reg_free(reg);
540 static void tmp_mv(struct tmp *t, int reg)
542 reg_for(reg, t);
543 tmp_reg(t, reg, 0);
546 static void tmp_to(struct tmp *t, int reg)
548 reg_for(reg, t);
549 tmp_reg(t, reg, 1);
552 static void tmp_drop(int n)
554 int i;
555 for (i = ntmp - n; i < ntmp; i++)
556 if (tmps[i].loc == LOC_REG)
557 regs[tmps[i].addr] = NULL;
558 ntmp -= n;
561 static void tmp_pop(int reg)
563 struct tmp *t = TMP(0);
564 tmp_to(t, reg);
565 tmp_drop(1);
568 static struct tmp *tmp_new(void)
570 return &tmps[ntmp++];
573 static void tmp_push(int reg)
575 struct tmp *t = tmp_new();
576 t->addr = reg;
577 t->bt = 0;
578 t->loc = LOC_REG;
579 regs[reg] = t;
582 void o_local(long addr)
584 struct tmp *t = tmp_new();
585 t->addr = -addr;
586 t->loc = LOC_LOCAL;
587 t->bt = 0;
588 t->off = 0;
591 void o_num(long num)
593 struct tmp *t = tmp_new();
594 t->addr = num;
595 t->bt = 0;
596 t->loc = LOC_NUM;
599 void o_sym(char *name)
601 struct tmp *t = tmp_new();
602 strcpy(t->sym, name);
603 t->loc = LOC_SYM;
604 t->bt = 0;
605 t->off = 0;
608 void o_tmpdrop(int n)
610 if (n == -1 || n > ntmp)
611 n = ntmp;
612 tmp_drop(n);
613 if (!ntmp) {
614 if (tmpsp != -1)
615 sp = tmpsp;
616 tmpsp = -1;
620 #define FORK_REG 0x00
622 /* make sure tmps remain intact after a conditional expression */
623 void o_fork(void)
625 int i;
626 for (i = 0; i < ntmp - 1; i++)
627 tmp_mem(&tmps[i]);
630 void o_forkpush(void)
632 tmp_pop(FORK_REG);
635 void o_forkjoin(void)
637 tmp_push(FORK_REG);
640 void o_tmpswap(void)
642 struct tmp *t1 = TMP(0);
643 struct tmp *t2 = TMP(1);
644 struct tmp t;
645 memcpy(&t, t1, sizeof(t));
646 memcpy(t1, t2, sizeof(t));
647 memcpy(t2, &t, sizeof(t));
648 if (t1->loc == LOC_REG)
649 regs[t1->addr] = t1;
650 if (t2->loc == LOC_REG)
651 regs[t2->addr] = t2;
654 static int reg_get(int mask)
656 int i;
657 for (i = 0; i < ARRAY_SIZE(tmpregs); i++)
658 if ((1 << tmpregs[i]) & mask && !regs[tmpregs[i]])
659 return tmpregs[i];
660 for (i = 0; i < ARRAY_SIZE(tmpregs); i++)
661 if ((1 << tmpregs[i]) & mask) {
662 reg_free(tmpregs[i]);
663 return tmpregs[i];
665 return 0;
668 static void tmp_copy(struct tmp *t1)
670 struct tmp *t2 = tmp_new();
671 memcpy(t2, t1, sizeof(*t1));
672 if (!(t1->loc & (LOC_REG | LOC_MEM)))
673 return;
674 if (t1->loc == LOC_MEM) {
675 tmp_reg(t2, reg_get(~0), 0);
676 } else if (t1->loc == LOC_REG) {
677 t2->addr = TMP_REG2(t2, t1->addr);
678 i_mov(I_MOV, t2->addr, t1->addr);
679 regs[t2->addr] = t2;
683 void o_tmpcopy(void)
685 tmp_copy(TMP(0));
688 void o_cast(unsigned bt)
690 struct tmp *t = TMP(0);
691 if (t->bt) {
692 t->bt = bt;
693 return;
695 if (t->loc == LOC_NUM) {
696 num_cast(t, bt);
697 return;
699 if (BT_SZ(bt) != LONGSZ) {
700 int reg = TMP_REG(t);
701 tmp_to(t, reg);
702 if (bt & BT_SIGNED)
703 i_sx(reg, BT_SZ(bt) * 8);
704 else
705 i_zx(reg, BT_SZ(bt) * 8);
709 void o_func_beg(char *name, int global)
711 out_sym(name, (global ? OUT_GLOB : 0) | OUT_CS, cslen, 0);
712 i_prolog();
713 sp = 0;
714 maxsp = sp;
715 ntmp = 0;
716 tmpsp = -1;
717 nret = 0;
718 memset(regs, 0, sizeof(regs));
721 void o_deref(unsigned bt)
723 struct tmp *t = TMP(0);
724 if (t->bt)
725 tmp_to(t, TMP_REG(t));
726 t->bt = bt;
729 void o_load(void)
731 struct tmp *t = TMP(0);
732 tmp_to(t, TMP_REG(t));
735 #define TMP_NUM(t) ((t)->loc == LOC_NUM && !(t)->bt)
736 #define LOCAL_PTR(t) ((t)->loc == LOC_LOCAL && !(t)->bt)
737 #define SYM_PTR(t) ((t)->loc == LOC_SYM && !(t)->bt)
739 int o_popnum(long *c)
741 struct tmp *t = TMP(0);
742 if (!TMP_NUM(t))
743 return 1;
744 *c = t->addr;
745 tmp_drop(1);
746 return 0;
749 void o_ret(int rets)
751 if (rets)
752 tmp_pop(REG_RET);
753 else
754 i_num(REG_RET, 0);
755 ret[nret++] = o_jmp(0);
758 void o_func_end(void)
760 int i;
761 for (i = 0; i < nret; i++)
762 o_filljmp(ret[i]);
763 i_epilog();
766 long o_mklocal(int size)
768 return sp_push(ALIGN(size, LONGSZ));
771 void o_rmlocal(long addr, int sz)
773 sp = addr - sz;
776 long o_arg(int i)
778 return -(10 + i) << 2;
781 void o_assign(unsigned bt)
783 struct tmp *t1 = TMP(0);
784 struct tmp *t2 = TMP(1);
785 int r1 = TMP_REG(t1);
786 int r2 = TMP_REG2(t2, r1);
787 int off = 0;
788 tmp_to(t1, r1);
789 if (t2->bt)
790 tmp_to(t2, r2);
791 if (t2->loc == LOC_LOCAL) {
792 r2 = REG_FP;
793 off = t2->addr + t2->off;
794 } else {
795 tmp_mv(t2, r2);
797 tmp_drop(2);
798 i_ldr(0, r1, r2, off, bt);
799 tmp_push(r1);
802 static long cu(int op, long i)
804 switch (op) {
805 case O_NEG:
806 return -i;
807 case O_NOT:
808 return ~i;
809 case O_LNOT:
810 return !i;
812 return 0;
815 static int c_uop(int op)
817 struct tmp *t1 = TMP(0);
818 if (!TMP_NUM(t1))
819 return 1;
820 tmp_drop(1);
821 o_num(cu(op, t1->addr));
822 return 0;
825 static long cb(int op, long a, long b)
827 switch (op) {
828 case O_ADD:
829 return a + b;
830 case O_SUB:
831 return a - b;
832 case O_AND:
833 return a & b;
834 case O_OR:
835 return a | b;
836 case O_XOR:
837 return a ^ b;
838 case O_MUL:
839 return a * b;
840 case O_DIV:
841 return a / b;
842 case O_MOD:
843 return a % b;
844 case O_SHL:
845 return a << b;
846 case O_SHR:
847 return (unsigned long) a >> b;
848 case O_ASR:
849 return a >> b;
850 case O_LT:
851 return a < b;
852 case O_GT:
853 return a > b;
854 case O_LE:
855 return a <= b;
856 case O_GE:
857 return a >= b;
858 case O_EQ:
859 return a == b;
860 case O_NEQ:
861 return a != b;
863 return 0;
866 static int c_bop(int op)
868 struct tmp *t1 = TMP(0);
869 struct tmp *t2 = TMP(1);
870 int locals = LOCAL_PTR(t1) + LOCAL_PTR(t2);
871 int syms = SYM_PTR(t1) + SYM_PTR(t2);
872 int nums = TMP_NUM(t1) + TMP_NUM(t2);
873 if (syms + locals == 2 || syms + nums + locals != 2)
874 return 1;
875 if (nums == 1)
876 if ((op != O_ADD && op != O_SUB) || (op == O_SUB && TMP_NUM(t2)))
877 return 1;
878 if (nums == 1) {
879 long o1 = TMP_NUM(t1) ? t1->addr : t1->off;
880 long o2 = TMP_NUM(t2) ? t2->addr : t2->off;
881 long ret = cb(op, o2, o1);
882 if (!TMP_NUM(t1))
883 o_tmpswap();
884 t2->off = ret;
885 tmp_drop(1);
886 } else {
887 long ret = cb(op, t2->addr, t1->addr);
888 tmp_drop(2);
889 o_num(ret);
891 return 0;
894 void o_uop(int op)
896 int r1 = TMP_REG(TMP(0));
897 if (!c_uop(op))
898 return;
899 tmp_to(TMP(0), r1);
900 switch (op) {
901 case O_NEG:
902 i_neg(r1);
903 break;
904 case O_NOT:
905 i_not(r1);
906 break;
907 case O_LNOT:
908 i_lnot(r1);
909 break;
913 static void bin_regs(int *r1, int *r2)
915 struct tmp *t2 = TMP(0);
916 struct tmp *t1 = TMP(1);
917 *r2 = TMP_REG(t2);
918 tmp_to(t2, *r2);
919 *r1 = TMP_REG2(t1, *r2);
920 tmp_pop(*r2);
921 tmp_pop(*r1);
924 static void bin_add(int op)
926 /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */
927 static int rx[] = {I_ADD, I_SUB, I_AND, I_ORR, I_EOR};
928 int r1, r2;
929 bin_regs(&r1, &r2);
930 i_add(rx[op], r1, r1, r2);
931 tmp_push(r1);
934 static void bin_shx(int op)
936 /* shl, shr */
937 int shx[] = {SM_LSL, SM_LSR, SM_ASR};
938 int r1, r2;
939 bin_regs(&r1, &r2);
940 i_shl(shx[op & 0x0f], r1, r1, r2);
941 tmp_push(r1);
944 static int log2a(unsigned long n)
946 int i = 0;
947 for (i = 0; i < LONGSZ * 8; i++)
948 if (n & (1u << i))
949 break;
950 if (i == LONGSZ * 8 || !(n >> (i + 1)))
951 return i;
952 return -1;
955 /* optimized version of mul/div/mod for powers of two */
956 static int mul_2(int op)
958 struct tmp *t1 = TMP(0);
959 struct tmp *t2 = TMP(1);
960 long n;
961 int r2;
962 int p;
963 if (op == O_MUL && t2->loc == LOC_NUM && !t2->bt)
964 o_tmpswap();
965 if (t1->loc != LOC_NUM || t1->bt)
966 return 1;
967 n = t1->addr;
968 p = log2a(n);
969 if (n && p == -1)
970 return 1;
971 if (op == O_MUL) {
972 tmp_drop(1);
973 if (n == 1)
974 return 0;
975 if (n == 0) {
976 tmp_drop(1);
977 o_num(0);
978 return 0;
980 r2 = TMP_REG(t2);
981 tmp_to(t2, r2);
982 i_shl_imm(SM_LSL, r2, p);
983 return 0;
985 if (op == O_DIV) {
986 tmp_drop(1);
987 if (n == 1)
988 return 0;
989 r2 = TMP_REG(t2);
990 tmp_to(t2, r2);
991 i_shl_imm(SM_LSR, r2, p);
992 return 0;
994 if (op == O_MOD) {
995 tmp_drop(1);
996 if (n == 1) {
997 tmp_drop(1);
998 o_num(0);
999 return 0;
1001 r2 = TMP_REG(t2);
1002 tmp_to(t2, r2);
1003 i_zx(r2, p);
1004 return 0;
1006 return 1;
1009 static void bin_mul(int op)
1011 int r1, r2;
1012 if (!mul_2(op))
1013 return;
1014 bin_regs(&r1, &r2);
1015 if (op == O_DIV || op == O_MOD)
1016 printf("div not implemented\n");
1017 i_mul(r1, r1, r2);
1018 tmp_push(r1);
1021 static void bin_cmp(int op)
1023 /* lt, gt, le, ge, eq, neq */
1024 static int cond[] = {11, 12, 13, 10, 0, 1};
1025 int r1, r2;
1026 bin_regs(&r1, &r2);
1027 i_cmp(I_CMP, r1, r2);
1028 i_set(cond[op & 0x0f], r1);
1029 tmp_push(r1);
1032 void o_bop(int op)
1034 if (!c_bop(op))
1035 return;
1036 if ((op & 0xf0) == 0x00)
1037 bin_add(op);
1038 if ((op & 0xf0) == 0x10)
1039 bin_shx(op);
1040 if ((op & 0xf0) == 0x20)
1041 bin_mul(op);
1042 if ((op & 0xf0) == 0x30)
1043 bin_cmp(op);
1046 static void load_regs2(int *r0, int *r1, int *r2)
1048 struct tmp *t0 = TMP(0);
1049 struct tmp *t1 = TMP(1);
1050 struct tmp *t2 = TMP(2);
1051 *r0 = TMP_REG(t0);
1052 *r1 = TMP_REG2(t1, *r0);
1053 *r2 = TMP_REG3(t2, *r0, *r1);
1054 tmp_to(t0, *r0);
1055 tmp_to(t1, *r1);
1056 tmp_to(t2, *r2);
1059 void o_memcpy(void)
1061 int rd, rs, rn;
1062 load_regs2(&rn, &rs, &rd);
1063 i_memcpy(rd, rs, rn);
1064 tmp_drop(2);
1067 void o_memset(void)
1069 int rd, rs, rn;
1070 load_regs2(&rn, &rs, &rd);
1071 i_memset(rd, rs, rn);
1072 tmp_drop(2);
1075 long o_mklabel(void)
1077 return cslen;
1080 static long jxz(long addr, int z)
1082 int r = TMP_REG(TMP(0));
1083 tmp_pop(r);
1084 i_b_if(addr, r, z);
1085 return cslen - 4;
1088 long o_jz(long addr)
1090 return jxz(addr, 1);
1093 long o_jnz(long addr)
1095 return jxz(addr, 0);
1098 long o_jmp(long addr)
1100 i_b(addr);
1101 return cslen - 4;
1104 void o_filljmp2(long addr, long jmpdst)
1106 i_b_fill((void *) cs + addr, jmpdst - addr);
1109 void o_filljmp(long addr)
1111 o_filljmp2(addr, cslen);
1114 void o_call(int argc, int rets)
1116 struct tmp *t;
1117 int i;
1118 int aregs = MIN(ARRAY_SIZE(argregs), argc);
1119 for (i = 0; i < ARRAY_SIZE(tmpregs); i++)
1120 if (regs[tmpregs[i]] && regs[tmpregs[i]] - tmps < ntmp - argc)
1121 tmp_mem(regs[tmpregs[i]]);
1122 if (argc > aregs) {
1123 sp_push(LONGSZ * (argc - aregs));
1124 for (i = argc - 1; i >= aregs; --i) {
1125 int reg = TMP_REG(TMP(0));
1126 tmp_pop(reg);
1127 i_ldr(0, reg, REG_SP, (i - aregs) * LONGSZ, LONGSZ);
1130 for (i = aregs - 1; i >= 0; --i)
1131 tmp_to(TMP(aregs - i - 1), argregs[i]);
1132 tmp_drop(aregs);
1133 t = TMP(0);
1134 if (t->loc == LOC_SYM && !t->bt) {
1135 i_call(t->sym);
1136 tmp_drop(1);
1137 } else {
1138 int reg = t->loc == LOC_REG ? t->addr : REG_TMP;
1139 tmp_pop(reg);
1140 i_call_reg(reg);
1142 if (rets)
1143 tmp_push(REG_RET);
1146 int o_nogen(void)
1148 return nogen++;
1151 void o_dogen(void)
1153 nogen = 0;
1156 void dat_bss(char *name, int size, int global)
1158 out_sym(name, OUT_BSS | (global ? OUT_GLOB : 0), bsslen, size);
1159 bsslen += ALIGN(size, LONGSZ);
1162 #define MAXDATS (1 << 10)
1163 static char dat_names[MAXDATS][NAMELEN];
1164 static int dat_offs[MAXDATS];
1165 static int ndats;
1167 void err(char *msg);
1168 void *dat_dat(char *name, int size, int global)
1170 void *addr = ds + dslen;
1171 int idx = ndats++;
1172 if (idx >= MAXDATS)
1173 err("nomem: MAXDATS reached!\n");
1174 strcpy(dat_names[idx], name);
1175 dat_offs[idx] = dslen;
1176 out_sym(name, OUT_DS | (global ? OUT_GLOB : 0), dslen, size);
1177 dslen += ALIGN(size, LONGSZ);
1178 return addr;
1181 static int dat_off(char *name)
1183 int i;
1184 for (i = 0; i < ndats; i++)
1185 if (!strcmp(name, dat_names[i]))
1186 return dat_offs[i];
1187 return 0;
1190 void o_datset(char *name, int off, unsigned bt)
1192 struct tmp *t = TMP(0);
1193 int sym_off = dat_off(name) + off;
1194 if (t->loc == LOC_NUM && !t->bt) {
1195 num_cast(t, bt);
1196 memcpy(ds + sym_off, &t->addr, BT_SZ(bt));
1198 if (t->loc == LOC_SYM && !t->bt) {
1199 out_rel(t->sym, OUT_DS, sym_off);
1200 memcpy(ds + sym_off, &t->off, BT_SZ(bt));
1202 tmp_drop(1);
1205 void o_write(int fd)
1207 out_write(fd, cs, cslen, ds, dslen);